Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Extends Developer Experience
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Yosuke Kurami
February 23, 2020
Programming
3.1k
5
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Extends Developer Experience
TypeScriptとDXのお話
Yosuke Kurami
February 23, 2020
More Decks by Yosuke Kurami
See All by Yosuke Kurami
TypeScript LSP の今までとこれから
quramy
1
2.2k
フロントエンドテストの育て方
quramy
12
3.9k
App Router 悲喜交々
quramy
8
770
上手に付き合うコンポーネントテスト
quramy
6
2.4k
Patched fetch did not work
quramy
6
810
GraphQL あるいは React における自律的なデータ取得について
quramy
18
6k
Next.js App Router
quramy
15
4k
Fragment Composition of GraphQL
quramy
17
4.9k
reg-viz VRT tools
quramy
4
1.8k
Other Decks in Programming
See All in Programming
C#の現在地 進化の歴史と、AI時代の.NET Everywhere
neuecc
4
3.7k
標準パッケージに uuid が追加された 背景から見る Go らしい意思決定 / go_127_uuid_decision
convto
5
8.3k
大喜利で理解するLLM as a Judge / Understanding LLM-as-a-Judge through Ogiri
rockname
0
160
選挙速報を多くのユーザーへ 届ける Live Activities 設計
hamayokokuririn
0
160
AWSに止められる覚悟してますか?
morizo_1984
1
220
モジュールの視点からSwiftを読み解く #iosdc
s_shimotori
0
190
スマート反転とウェブアクセシビリティ
camiha
0
210
JPUG勉強会 OSSデータベースの内部構造を理解しよう(第2回)
oga5
0
270
iOSDC Japan 2026 - Swiftで作って学ぼう!データベース自作入門
kaseken
2
340
変化を抱擁するドキュメントの作り方 - ビジネスルール駆動開発がもたらす、コードとの新しい関係
ioki
2
220
Ghostty + Neovimで作る 透明でカッコ良い開発環境
j341nono
0
120
Heart of Swift Concurrency
koher
0
970
Featured
See All Featured
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
920
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
280
The SEO Collaboration Effect
kristinabergwall1
1
570
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.8k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.6k
Agile that works and the tools we love
rasmusluckow
331
22k
A Tale of Four Properties
chriscoyier
163
24k
[RailsConf 2023] Rails as a piece of cake
palkan
59
7k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
500
Crafting Experiences
bethany
1
350
AI: The stuff that nobody shows you
jnunemaker
PRO
10
1.1k
Transcript
Extends Developer Experience Yosuke Kurami (@Quramy)
About me - Yosuke KuramiʢGitHub, Twitter: @Quramyʣ - Web frontend
developer - 5͘Β͍TypeScriptͬͯ·͢
None
TypeScript APIΛͬͨ πʔϧ࡞Γָ͍ͧ͠
Today’s goal - TypeScriptΛͬͨπʔϧͰշద͞ΛखʹೖΕΑ͏ʂ - ͦΜͳ͜ͱͰ͖Δͷʁͱ͍͏ؾ͖ʹͳͬͯ͘ΕΔͱخ͍͠ - TypeScript APIͰԿ͔࡞Ζ͏ʂͱ͍͏ਓ͕૿͑ͯཉ͍͠
TypeScript and DX
What’s DX ? - ͦͦDeveloper ExperienceͬͯͳΜͩΖ͏ - ͱͱUser ExperienceʹΠϯεύΠΞ͞Εͨݴ༿ -
ʮ։ൃऀ͕ετϨεແ͘γεςϜΛ։ൃɾอकͰ͖Δ͜ͱʯ - ࣗಈԽɺ͔Γ͍͢APIυΩϡϝϯτɺetc...
Bad DX example - ࢼ͠ʹʮDX͕ѱ͍ঢ়ଶʯΛߟ͑ͯΈΔ - ࣍ͷJavaScriptͷίʔυ͕͋ͬͨͱͯ͠ɿ function fn(someData) {
return data.response; }
Bad DX example - ͜ͷίʔυͷةݥੑ: - data is undefined -
responsedataͷkeyͱͯ͠ଘࡏ͍ͯ͠Δͷ͔ʁ function fn(someData) { return data.response; }
Bad DX example - ͠ؾ͔ͣʹຊ൪ྲྀग़ͯ͠োʹͳͬͨͱ͢Δͱɿ - Өڹௐࠪ - hot fixͷ࡞/ϨϏϡʔ/σϓϩΠ
- ৼΓฦΓ - ߦͷमਖ਼Ͱ͋ͬͯɺ࣌ؒͱMP͕େྔʹΒΕΔ
With TypeScript - TypeScriptͰΧόʔͰ͖Δ͜ͱ: - ੩తղੳʹΑΔΤϥʔͷࣄલݕ - ར༻ՄೳͳϓϩύςΟͷࣗಈิ - etc…
- ʮόάΛগͳͤ͘͞Δʯͱ͍͏ΞϑΥʔμϯεΛܗ͍ͯ͠Δ function fn(someData: DataType) { return someData.response; }
Predictability - TypeScript͕JavaScriptΤϯδχΞʹͨΒͨ͠ͷɿ༧ଌՄೳੑ - TypeScripterʹͱͬͯʮDXΛ֦ு͢Δʯͱɿ - ༧ଌՄೳͳྖҬΛ͛Δ͜ͱ
The unknown - TypeScript͕༧ଌͣ͠Β͍ྖҬ - JavaScriptҎ֎ͷΤίγεςϜͱͷ࿈ܞ - e.g. API req/res,
database access, CSS / HTML template, etc... - ԿʹunknownΛݮΒ͔͕͢DX্ͷ伴
1. Code generation
Automatic type generation - ֎෦͔Β unknown ͕ͬͯ͘Δ - TypeScriptͷܕఆٛΛػցతʹ༩͑ͯ͠·͑ʂ
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 () => ( <div className={styles.card}> <span className={styles.title}>Title</span> </div> );
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 () => ( <div className={styles.card}> <span className={styles.title}>Title</span> </div> ); /* Card.css.d.ts */ declare const styles: { readonly "card": string; readonly "title": string; }; export = styles
Case 1-2. GraphQL data type - GraphQLͷಛ - ੩తʹܕ͚͞ΕͨSchema͕ଘࡏ͢Δ -
ʢSchemaͰڐ༰͞ΕΔൣғͰʣࣗ༝ʹσʔλΛ͍߹ΘͤՄೳ
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<QueryResult>({ query: myQuery }); console.log(data?.viewer?.repositories);
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; }; }; };
Code generation flow ίʔυੜͷྲྀΕɿ - ผݴޠΛղੳͯ͠ߏԽ͞ΕͨใΛಘΔ - e.g. GraphQL Schema,
CSS Selector, Flow Type (Babylon) - ߏԽใΛݩʹTypeScriptͷASTʢநߏจʣΛ࡞Δ - ASTΛςΩετදݱʹͯ͠ɺϑΝΠϧͱͯ͠อଘ͢Δ
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 )]), );
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);
Tips - TypeScript ASTͱίʔυͷؔΛݟΔʹAST viewer͕͓͢͢Ί - https://ts-ast-viewer.com - ASTΛੜ͢ΔͨΊͷίʔυεχϖοτදࣔͯ͘͠ΕΔ
TS AST viewer
2. Editor extension
Where can I check ? - ࠓͷΩʔϫʔυʮ༧ଌՄೳੑʯ - Ͳͷ࣌ͰΤϥʔΛ༧ଌ͢Δʁ CI
or Git client hook or ... - ૣظʹ༧ଌͰ͖ΕͰ͖Δ΄ͲʮDX͕ߴ͍ʯͣ - Α͠ΤσΟλͰͬͯ͠·͓͏
Language service plugin - Language serviceɿΤσΟλʹΤϥʔใิީิΛಧ͚Δׂ ʢVSCͰαΫαΫίʔυ͕ॻ͚ΔͷίΠπͷ͓ӄʣ - Language serviceʹϢʔβʔಠࣗͷplugin͕ઃఆՄೳ
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" } ] } }
None
Case 2-2. ESLint - https://github.com/Quramy/typescript-eslint-language-service -
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
Advantages of language service - ΤσΟλͷछผΛΘͳ͍ - VSCͩͷvimͩͷemacsͩͷɺफڭ૪ʹؔΘΒͳͯ͘ࡁΉ
Advantages of language service - TypeScript server (a.k.a. tsserver) ͷԸܙ
- RopeʹΑΔString࣮ɺ૿parseͳͲߴͳج൫ https://quramy.github.io/ts-server-side-anatomy/ - ΤσΟλଆͰ֦ுΛ࡞ΔΑΓߴͳΠϯλϥΫγϣϯΛ࣮ݱՄೳ - ͚Ε͍΄Ͳྑ͍
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 });
How to create a plugin ? Language serviceͷ֦ுϙΠϯτʢҰ෦ൈਮʣɿ getCompleteAtPosition ࣗಈิީิͷฦ٫
getSemanticDiagnostics Τϥʔใͷฦ٫ getDefinitionAtPosition ఆٛՕॴδϟϯϓ getQuickInfoAtPosition πʔϧνοϓ
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, }; }
Plugin method code flow - ϑοΫͨ͠ϝιουͷதͰɺTypeScript ASTΛղੳ͢Δ - ྫ: -
GraphQLͷέʔεɿTemplate string literalΛநग़͠ɺgraphql-jsͰղ ੳޙɺ݁ՌΛฦ٫͢Δ - ESLintͷέʔεɿTypeScript ASTΛestreeʹม͠ɺlinterΛݺͼग़͢
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); }
Let’s create your plugin - TypeScript language service pluginͦͦ͜͜ϚχΞοΫͳΈ͚ͩ Ͳɺͬͱ·ͬͯཉ͍͠
- TypeScriptؔ࿈ͷVSC֦ு࡞Λߟ͍͑ͯΔͷͰ͋Εɺlanguage service pluginݕ౼ͯ͠Έͯ
3. Transformation
I talked... Code generation: Writing AST Editor extension: Reading AST
TypeScriptίʔυͷॻ͖͑ Custom transformation
What’s transformer ? - ASTΛผͷASTʹม͢ΔΈ - Babel pluginͷTypeScript൛ - TypeScriptຊମtransformerͷΈ߹ΘͤͰͰ͖͍ͯΔ
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
Caveat - Custom transformertscʹઃఆͰ͖ͳ͍ APIܦ༝Ͱར༻͢Δඞཁ͕͋Δ - ϝδϟʔͳϏϧυπʔϧCustom transformer͕ઃఆՄೳ - e.g.
ts-loader, awesome-typescript-loader, gulp-typescript, etc...
Transformer - Custom transformerࣗମʮDXΛߴΊΔʯʹد༩͠ͳ͍ - ੜ͞ΕΔ.jsίʔυʹ৵ऻ͢ΔͨΊɺΉ͠ΖUXʹӨڹ͢Δ͜ͱ
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` <div>Hello, ${name}</div> `;
Case 3-1. AoT - ͜ͷྨͷϑϨʔϜϫʔΫͰɺDSLݻ༗ͷதؒදݱʹςϯϓϨʔτΛί ϯύΠϧͯ͠࠶ར༻͢ΔΑ͏ʹઃܭ͞Ε͍ͯΔέʔε͕ଟ͍ - ͦͷ··ϒϥβʹ͍࣋ͬͯ͘ͱɺੑೳΛྼԽͤ͞Δ͜ͱ - ࣮ߦ࣌parseͷΦʔόʔϔου
- parserͦͷͷؚ͕·ΕΔ͜ͱʹΑΓɺbundleαΠζ͕૿େ
Case 3-1. AoT - ͦ͜ͰAhead of Time compile - Ϗϧυ࣌ʹTemplate
literalͷதΛparseͯ͠தؒදݱʹม - AngularͷngcίϚϯυͳͲ͕༗໊ - https://github.com/Quramy/ts-graphql-plugin ͰGraphQL ΫΤϦΛ AoTͰม͢ΔͨΊͷCustom transformerΛఏڙ
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); }
Other use cases - ιʔείʔυதͷtype aliasinterfaceͷใΛ࠷ऴతͳ.jsϑΝΠϧʹ࣋ ͪग़͢͜ͱՄೳ - https://github.com/angular/tsickle -
TypeScriptͷܕใΛGoogle Closure CompilerͷJSDocʹม͢Δ
Summary
Summary - TypeScriptͰ֫ಘ͢ΔDX: ʮ༧ଌՄೳੑʯ - TypeScript APIͰ༧ଌՄೳੑΛఈ্͛͢Δ - .tsίʔυΛੜͯ͠unknownʹܕΛ༩͑Δ -
.tsίʔυΛղੳͯ͠ΤσΟλʹใΛಧ͚Δ - ղੳ/ੜͷΈΛ࠶ར༻͢ΕɺύϑΥʔϚϯε্ʹܨ͕Δ͜ͱ
Getting started - TypeScriptͷAPI͍ͬͺ͍͋Δ͚Ͳʢࠓհͨ͠ͷ͘͝Ұ෦ʣ - ·ͣASTʹ׳ΕΔͷ͕Φεεϝ - ࣍ʹTypeCheckerͳͲͷCompiler APIͱLangage Service
API - ASTͷಡΈॻ͖ʹ׳ΕͨΒTransformer API؆୯ - ASTղੳͷࣝTypeScriptҎ֎Ͱʹཱͭʢe.g. ESLint rulesʣ
TypeScript APIΛͬͨ πʔϧ࡞Γָ͍ͧ͠
Thank you !