Slide 1

Slide 1 text

Extends Developer Experience Yosuke Kurami (@Quramy)

Slide 2

Slide 2 text

About me - Yosuke KuramiʢGitHub, Twitter: @Quramyʣ - Web frontend developer - 5೥͘Β͍TypeScript΍ͬͯ·͢

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

TypeScript APIΛ࢖ͬͨ πʔϧ࡞Γ͸ָ͍ͧ͠

Slide 5

Slide 5 text

Today’s goal - TypeScriptΛ࢖ͬͨπʔϧͰշద͞ΛखʹೖΕΑ͏ʂ - ͦΜͳ͜ͱͰ͖Δͷʁͱ͍͏ؾ෇͖ʹͳͬͯ͘ΕΔͱخ͍͠ - TypeScript APIͰԿ͔࡞Ζ͏ʂͱ͍͏ਓ͕૿͑ͯཉ͍͠

Slide 6

Slide 6 text

TypeScript and DX

Slide 7

Slide 7 text

What’s DX ? - ͦ΋ͦ΋Developer ExperienceͬͯͳΜͩΖ͏ - ΋ͱ΋ͱ͸User ExperienceʹΠϯεύΠΞ͞Εͨݴ༿ - ʮ։ൃऀ͕ετϨεແ͘γεςϜΛ։ൃɾอकͰ͖Δ͜ͱʯ - ࣗಈԽɺ෼͔Γ΍͍͢APIυΩϡϝϯτɺetc...

Slide 8

Slide 8 text

Bad DX example - ࢼ͠ʹʮDX͕ѱ͍ঢ়ଶʯΛߟ͑ͯΈΔ - ࣍ͷJavaScriptͷίʔυ͕͋ͬͨͱͯ͠ɿ function fn(someData) { return data.response; }

Slide 9

Slide 9 text

Bad DX example - ͜ͷίʔυͷةݥੑ: - data is undefined - response͸dataͷkeyͱͯ͠ଘࡏ͍ͯ͠Δͷ͔ʁ function fn(someData) { return data.response; }

Slide 10

Slide 10 text

Bad DX example - ΋͠ؾ෇͔ͣʹຊ൪ྲྀग़ͯ͠ো֐ʹͳͬͨͱ͢Δͱɿ - Өڹௐࠪ - hot fixͷ࡞੒/ϨϏϡʔ/σϓϩΠ - ৼΓฦΓ - ਺ߦͷमਖ਼Ͱ͋ͬͯ΋ɺ࣌ؒͱMP͕େྔʹ࡟ΒΕΔ

Slide 11

Slide 11 text

With TypeScript - TypeScriptͰΧόʔͰ͖Δ͜ͱ: - ੩తղੳʹΑΔΤϥʔͷࣄલݕ஌ - ར༻ՄೳͳϓϩύςΟͷࣗಈิ׬ - etc… - ʮόάΛগͳͤ͘͞Δʯͱ͍͏ΞϑΥʔμϯεΛܗ੒͍ͯ͠Δ function fn(someData: DataType) { return someData.response; }

Slide 12

Slide 12 text

Predictability - TypeScript͕JavaScriptΤϯδχΞʹ΋ͨΒͨ͠΋ͷɿ༧ଌՄೳੑ - TypeScripterʹͱͬͯʮDXΛ֦ு͢Δʯͱ͸ɿ - ༧ଌՄೳͳྖҬΛ޿͛Δ͜ͱ

Slide 13

Slide 13 text

The unknown - TypeScript͕༧ଌͣ͠Β͍ྖҬ - JavaScriptҎ֎ͷΤίγεςϜͱͷ࿈ܞ - e.g. API req/res, database access, CSS / HTML template, etc... - ೗ԿʹunknownΛݮΒ͔͕͢DX޲্ͷ伴

Slide 14

Slide 14 text

1. Code generation

Slide 15

Slide 15 text

Automatic type generation - ֎෦͔Β unknown ͕΍ͬͯ͘Δ - TypeScriptͷܕఆٛΛػցతʹ༩͑ͯ͠·͑ʂ

Slide 16

Slide 16 text

Case 1-1. CSS - CSS Modules - scoped cssͷํ๏࿦ͷ1ͭɻclass໊ΛES2015 moduleͷΑ͏ʹѻ͏ /* Card.css */ .card { border-radius: 4px; padding: 16px; background-color: white; } .title { font-weight: bold; } /* Card.tsx */ import React from "react"; import styles from "./Card.css"; export default () => (
Title
);

Slide 17

Slide 17 text

Case 1-1. CSS - CSSʹରԠ͢Δ.d.tsΛ༻ҙ͢Ε͹ɺTypeScript͔ΒimportͰ͖Δ - https://github.com/Quramy/typed-css-modules /* Card.tsx */ import React from "react"; import styles from "./Card.css"; export default () => (
Title
); /* Card.css.d.ts */ declare const styles: { readonly "card": string; readonly "title": string; }; export = styles

Slide 18

Slide 18 text

Case 1-2. GraphQL data type - GraphQLͷಛ௃ - ੩తʹܕ෇͚͞ΕͨSchema͕ଘࡏ͢Δ - ʢSchemaͰڐ༰͞ΕΔൣғͰʣࣗ༝ʹσʔλΛ໰͍߹ΘͤՄೳ

Slide 19

Slide 19 text

Case 1-2. GraphQL data type import gql from "graphql-tag"; import { QueryResult } from "./__generated__/anonymous-query"; const myQuery = gql` query { viewer { repositories(last: 3) { nodes { name languages(first: 10) { nodes { name } } } } } } `; const { data } = await client.query({ query: myQuery }); console.log(data?.viewer?.repositories);

Slide 20

Slide 20 text

Case 1-2. GraphQL data type - QueryͱSchema৘ใΛݩʹɺ݁ՌͱͳΔ QueryResult typeΛࣗಈੜ੒ - https://github.com/Quramy/ts-graphql-plugin - /* eslint-disable */ /* This is an autogenerated file. Do not edit this file directly! */ export type QueryResult = { viewer: { repositories: { nodes: (({ name: string; languages: ({ nodes: (({ name: string; }) | null)[] | null; }) | null; }) | null)[] | null; }; }; };

Slide 21

Slide 21 text

Code generation flow ίʔυੜ੒ͷྲྀΕɿ - ผݴޠΛղੳͯ͠ߏ଄Խ͞Εͨ৘ใΛಘΔ - e.g. GraphQL Schema, CSS Selector, Flow Type (Babylon) - ߏ଄Խ৘ใΛݩʹTypeScriptͷASTʢந৅ߏจ໦ʣΛ࡞Δ - ASTΛςΩετදݱʹͯ͠ɺϑΝΠϧͱͯ͠อଘ͢Δ

Slide 22

Slide 22 text

Abstract syntax tree export type Data = { hoge: string; }; ts.createTypeAliasDeclaration( undefined, [ts.createModifier(ts.SyntaxKind.ExportKeyword)], ts.createIdentifier("Data"), undefined, ts.createTypeLiteralNode([ts.createPropertySignature( undefined, ts.createIdentifier("hoge"), undefined, ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined )]), );

Slide 23

Slide 23 text

Generate code from AST - ASTΛςΩετදݱʹunparse͢Δʹ͸ɺts.PrinterΛར༻͢Δ const node = ts.createTypeLiteralNode(...); const sourceText = ts.createPrinter().printNode( ts.EmitHint.Unspecified, node, ts.createSourceFile("generated.ts", "", ts.ScriptTarget.Latest), ); ts.sys.writeFile("generated.ts", sourceText);

Slide 24

Slide 24 text

Tips - TypeScript ASTͱίʔυͷؔ܎ΛݟΔʹ͸AST viewer͕͓͢͢Ί - https://ts-ast-viewer.com - ASTΛੜ੒͢ΔͨΊͷίʔυεχϖοτ΋දࣔͯ͘͠ΕΔ

Slide 25

Slide 25 text

TS AST viewer

Slide 26

Slide 26 text

2. Editor extension

Slide 27

Slide 27 text

Where can I check ? - ࠓ೔ͷΩʔϫʔυ͸ʮ༧ଌՄೳੑʯ - Ͳͷ࣌఺ͰΤϥʔΛ༧ଌ͢Δʁ CI or Git client hook or ... - ૣظʹ༧ଌͰ͖Ε͹Ͱ͖Δ΄ͲʮDX͕ߴ͍ʯ͸ͣ - Α͠ΤσΟλͰ΍ͬͯ͠·͓͏

Slide 28

Slide 28 text

Language service plugin - Language serviceɿΤσΟλʹΤϥʔ৘ใ΍ิ׬ީิΛಧ͚Δ໾ׂ ʢVSCͰαΫαΫίʔυ͕ॻ͚Δͷ΋ίΠπͷ͓ӄʣ - Language serviceʹ͸Ϣʔβʔಠࣗͷplugin͕ઃఆՄೳ

Slide 29

Slide 29 text

Case 2-2. GraphQL query - https://github.com/Quramy/ts-graphql-plugin - GraphQLͷΫΤϦهड़Λิॿ͢ΔLanguage service plugin /* tsconfig.json */
 
 { "compilerOptions": { "plugins": [ { "name": "ts-graphql-plugin", // plugin NPM package name "schema": "schema.graphql", "tag": "gql" } ] } }

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

Case 2-2. ESLint - https://github.com/Quramy/typescript-eslint-language-service -

Slide 32

Slide 32 text

Other plugins - Template string literalΛѻ͏ϥΠϒϥϦΛର৅ʹ࡞੒͞ΕΔ͜ͱ͕ଟ͍ - Angular: HTML templateதͷΤϥʔνΣοΫ΍ิ׬ https://www.npmjs.com/package/@angular/language-service - lit-html: HTML templateதͷิ׬ https://www.npmjs.com/package/ts-lit-plugin - styled-components: CSS attributesͷิ׬ https://www.npmjs.com/package/typescript-styled-plugin

Slide 33

Slide 33 text

Advantages of language service - ΤσΟλͷछผΛ໰Θͳ͍ - VSCͩͷvimͩͷemacsͩͷɺफڭ࿦૪ʹؔΘΒͳͯ͘ࡁΉ

Slide 34

Slide 34 text

Advantages of language service - TypeScript server (a.k.a. tsserver) ͷԸܙ - RopeʹΑΔString࣮૷΍ɺ૿෼parseͳͲߴ଎ͳج൫ https://quramy.github.io/ts-server-side-anatomy/ - ΤσΟλଆͰ֦ுΛ࡞ΔΑΓ΋ߴ଎ͳΠϯλϥΫγϣϯΛ࣮ݱՄೳ - ଎͚Ε͹଎͍΄Ͳྑ͍

Slide 35

Slide 35 text

How to create a plugin ? - ts.LanguageServiceΛϥοϓͯ͠ฦ٫͢Δؔ਺Λ࡞Δ͚ͩ import ts from "typescript/lib/tsserverlibrary"; function create(info: ts.server.PluginCreateInfo) { // Create a proxy for language service return wrapLangService(info.languageService); } export = () => ({ create });

Slide 36

Slide 36 text

How to create a plugin ? Language serviceͷ֦ுϙΠϯτʢҰ෦ൈਮʣɿ getCompleteAtPosition ࣗಈิ׬ީิͷฦ٫ getSemanticDiagnostics Τϥʔ৘ใͷฦ٫ getDefinitionAtPosition ఆٛՕॴ΁δϟϯϓ getQuickInfoAtPosition πʔϧνοϓ

Slide 37

Slide 37 text

Minimal plugin example function create(info: ts.server.PluginCreateInfo) { const getQuickInfoAtPosition = new Proxy( info.languageService.getQuickInfoAtPosition, { apply(delegate, self, args) { const result = delegate.apply(self, args); if (!result) return; return { ...result, displayParts: [ { text: "" }, ...result.displayParts || [], { text: "" }, ], } as typeof result; }, }, ); return { ...info.languageService, getQuickInfoAtPosition, }; }

Slide 38

Slide 38 text

Plugin method code flow - ϑοΫͨ͠ϝιουͷதͰɺTypeScript ASTΛղੳ͢Δ - ྫ: - GraphQLͷέʔεɿTemplate string literalΛநग़͠ɺgraphql-jsͰղ ੳޙɺ݁ՌΛฦ٫͢Δ - ESLintͷέʔεɿTypeScript ASTΛestreeʹม׵͠ɺlinterΛݺͼग़͢

Slide 39

Slide 39 text

AST traverser example - Template string literalΛ୳ࡧ͢Δίʔυͷྫ function getSemanticDiagnostics(fileName: string) { const src = langService.getProgram().getSourceFile(fileName)!; const processTemplate = (node: ts.Node) => { if (ts.isNoSubstitutionTemplateLiteral(node)) { const body = node.getText(); // process text body } else { ts.forEachChild(node, processTemplate); } } ts.forEachChild(src, processTemplate); }

Slide 40

Slide 40 text

Let’s create your plugin - TypeScript language service plugin͸ͦͦ͜͜ϚχΞοΫͳ࢓૊Έ͚ͩ Ͳɺ΋ͬͱ޿·ͬͯཉ͍͠ - TypeScriptؔ࿈ͷVSC֦ு࡞੒Λߟ͍͑ͯΔͷͰ͋Ε͹ɺlanguage service plugin΋ݕ౼ͯ͠Έͯ

Slide 41

Slide 41 text

3. Transformation

Slide 42

Slide 42 text

I talked... Code generation: Writing AST Editor extension: Reading AST TypeScriptίʔυͷॻ͖׵͑ Custom transformation

Slide 43

Slide 43 text

What’s transformer ? - ASTΛผͷASTʹม׵͢Δ࢓૊Έ - Babel pluginͷTypeScript൛ - TypeScriptຊମ΋transformerͷ૊Έ߹ΘͤͰͰ͖͍ͯΔ

Slide 44

Slide 44 text

Example 10 ** 3 Math.pow(10, 3) ts.createCall( ts.createPropertyAccess( ts.createIdentifier("Math"), ts.createIdentifier("pow") ), undefined, [ ts.createNumericLiteral("10"), ts.createNumericLiteral("3") ], ); ts.createBinary( ts.createNumericLiteral("10"), ts.createToken( ts.SyntaxKind.AsteriskAsteriskToken ), ts.createNumericLiteral("3"), ); Before After parse print Transform

Slide 45

Slide 45 text

Caveat - Custom transformer͸tscʹ͸ઃఆͰ͖ͳ͍ APIܦ༝Ͱར༻͢Δඞཁ͕͋Δ - ϝδϟʔͳϏϧυπʔϧ͸Custom transformer͕ઃఆՄೳ - e.g. ts-loader, awesome-typescript-loader, gulp-typescript, etc...

Slide 46

Slide 46 text

Transformer - Custom transformerࣗମ͸௚઀ʮDXΛߴΊΔʯʹ͸د༩͠ͳ͍ - ੜ੒͞ΕΔ.jsίʔυʹ৵ऻ͢ΔͨΊɺΉ͠ΖUXʹӨڹ͢Δ͜ͱ΋

Slide 47

Slide 47 text

Case 3-1. AoT - ඇJavaScriptͷDSLΛTemplate Stringͱͯ͠.jsʹ࣋ͪࠐΉϑϨʔϜϫʔΫ ͸৭ʑͱ͋Δ - Apollo Client(GraphQL), Angular HTML/CSS, Styled Components, lit- html, etc… const query = gql` query { viewer { repositories(last: 3) { nodes { name } } } } `; import { html } from 'lit-html'; const template = (name: string) => html`
Hello, ${name}
`;

Slide 48

Slide 48 text

Case 3-1. AoT - ͜ͷྨͷϑϨʔϜϫʔΫͰ͸ɺDSLݻ༗ͷதؒදݱʹςϯϓϨʔτΛί ϯύΠϧͯ͠࠶ར༻͢ΔΑ͏ʹઃܭ͞Ε͍ͯΔέʔε͕ଟ͍ - ͦͷ··ϒϥ΢βʹ͍࣋ͬͯ͘ͱɺੑೳΛྼԽͤ͞Δ͜ͱ΋ - ࣮ߦ࣌parseͷΦʔόʔϔου - parserͦͷ΋ͷؚ͕·ΕΔ͜ͱʹΑΓɺbundleαΠζ͕૿େ

Slide 49

Slide 49 text

Case 3-1. AoT - ͦ͜ͰAhead of Time compile - Ϗϧυ࣌ʹTemplate literalͷத਎Λparseͯ͠தؒදݱʹม׵ - AngularͷngcίϚϯυͳͲ͕༗໊ - https://github.com/Quramy/ts-graphql-plugin Ͱ΋GraphQL ΫΤϦΛ AoTͰม׵͢ΔͨΊͷCustom transformerΛఏڙ

Slide 50

Slide 50 text

Transformer example - Template string literalΛԿ͔ͷObject literalʹม׵͢Δྫ: - ར༻͢ΔAPI͸গ͠ҟͳΔ͕ɺAST୳ࡧؔ਺Λ࠶ؼݺͼग़͢͠Δͷ͸ Language service pluginͷέʔεͱҰॹ function transform(ctx: ts.TransformationContext) { const processTemplate = (node: ts.Node): ts.Node => { if (ts.isNoSubstitutionTemplateLiteral(node)) { return ts.createObjectLiteral(/* ... */); } else { return ts.visitEachChild(node, processTemplate, ctx); } }; return (src: ts.SourceFile) => ts.visitEachChild(src, processTemplate, ctx); }

Slide 51

Slide 51 text

Other use cases - ιʔείʔυதͷtype alias΍interfaceͷ৘ใΛ࠷ऴతͳ.jsϑΝΠϧʹ࣋ ͪग़͢͜ͱ΋Մೳ - https://github.com/angular/tsickle - TypeScriptͷܕ৘ใΛGoogle Closure CompilerͷJSDocʹม׵͢Δ

Slide 52

Slide 52 text

Summary

Slide 53

Slide 53 text

Summary - TypeScriptͰ֫ಘ͢ΔDX: ʮ༧ଌՄೳੑʯ - TypeScript APIͰ༧ଌՄೳੑΛఈ্͛͢Δ - .tsίʔυΛੜ੒ͯ͠unknownʹܕΛ༩͑Δ - .tsίʔυΛղੳͯ͠ΤσΟλʹ৘ใΛಧ͚Δ - ղੳ/ੜ੒ͷ࢓૊ΈΛ࠶ར༻͢Ε͹ɺύϑΥʔϚϯε޲্ʹܨ͕Δ͜ͱ΋

Slide 54

Slide 54 text

Getting started - TypeScriptͷAPI͸͍ͬͺ͍͋Δ͚Ͳʢࠓ೔঺հͨ͠ͷ΋͘͝Ұ෦ʣ - ·ͣ͸ASTʹ׳ΕΔͷ͕Φεεϝ - ࣍ʹTypeCheckerͳͲͷCompiler APIͱLangage Service API - ASTͷಡΈॻ͖ʹ׳ΕͨΒTransformer API͸؆୯ - ASTղੳͷ஌ࣝ͸TypeScriptҎ֎Ͱ΋໾ʹཱͭʢe.g. ESLint rulesʣ

Slide 55

Slide 55 text

TypeScript APIΛ࢖ͬͨ πʔϧ࡞Γ͸ָ͍ͧ͠

Slide 56

Slide 56 text

Thank you !