Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
型情報を手繰り寄せる技術~TypeScript Compiler APIによる型解析実践~
Search
jiko21
November 23, 2025
1.1k
0
Share
型情報を手繰り寄せる技術~TypeScript Compiler APIによる型解析実践~
TSKaigi Hokurikuの資料です
jiko21
November 23, 2025
More Decks by jiko21
See All by jiko21
Creating a Next.js-style Framework with Bun and Hono
jiko21
0
180
Array Grouping will soon be arriving at TypeScript
jiko21
0
160
Copying Array Methods arrived at TypeScript
jiko21
1
770
SSRで動的に OGP画像を生成したい! 〜Cloudflare Workersから@vercel/og移行編〜
jiko21
0
160
node:test will replace Jest?
jiko21
0
120
どこでも動かすために… TypeScriptでライブラリ開発の すゝめ
jiko21
2
430
NestJS a progressive web framework
jiko21
3
2.2k
レガシーなフロントエンドをリプレイスする
jiko21
5
1.6k
Deep Dive Into Vue Composition API
jiko21
0
3.3k
Featured
See All Featured
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
BBQ
matthewcrist
89
10k
HDC tutorial
michielstock
1
610
Mind Mapping
helmedeiros
PRO
1
150
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
140
The Illustrated Children's Guide to Kubernetes
chrisshort
51
52k
Art, The Web, and Tiny UX
lynnandtonic
304
21k
Designing for humans not robots
tammielis
254
26k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
199
73k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
160
Transcript
型情報を手繰り寄せる技術 ~TypeScript Compiler APIによる型解析実践~ TSKaigi Hokuriku 小島大基 (@jiko21) / エムスリー株式会社
自己紹介 • 名前: 小島大基(こじまだいき) / @jiko21 • 所属: エムスリー株式会社 •
北陸の思い出 • 2年前、大阪から金沢までサンダーバードを使って観光旅行に行きました • 金箔入りソフトクリームがおすすめ
突然ですが…
型から何かを生成したくないですか?
型から生成できるとうれしいもの • APIのSchema • IF定義(open-api)からの生成はあるが実装からIFを作りたいetc • テスト用ダミーデータの生成 • いつも自前で実装しているが型からfaker等利用して生成できると楽 •
その他いっぱい…
ワイ「型からHTML生成したい!」
型からHTMLを生成したいモチベーション • HTMLのセマンティクスやルールに沿ったHTMLだけを書ける状態にしたい • NGパターン1 inline要素の中に block要素は置けない
型からHTMLを生成したいモチベーション • HTMLのセマンティクスやルールに沿ったHTMLだけを書ける状態にしたい • NGパターン2 ブロック要素があると pタグは閉じられる ブロック要素があると pタグは閉じられる
こういうものを作りました!
html-typeについて • TypeScriptの型からHTMLを生成するライブラリ
html-typeについて • Pの中でDIVを利用するなど、HTMLのセマンティクスに違反する場合 型エラーとなる
html-typeのやっていること • 型の提供 • 各HTMLのタグに対応する型を提供 • Html<T>, Body<T>, Div<T>, P<T>
• TypeScript Compiler APIによる型解析
html-typeのやっていること • 型の提供 • 各HTMLのタグに対応する型を提供 • Html<T>, Body<T>, Div<T>, P<T>
• TypeScript Compiler APIによる型解析
TypeScript Compiler API
TypeScript Compiler APIって知ってます? • TypeScriptを解析するためのAPI • 型情報やコメント等をAST(抽象構文木)で取得できたり、型生成したりできる • 実際に使われているプロダクト •
ESLint • openapi-typescript • etc • TypeScript Goの登場でjsから直接触ることはできなくなる可能性があるので注意…
• TypeScriptの型をASTに変換する TypeScript Compiler APIでできること
html-typeのやっていること • 型の提供 • 各HTMLのタグに対応する型を提供 • Html<T>, Body<T>, Div<T>, P<T>
• TypeScript Compiler APIによる型解析
TypeScript Compiler APIを使っていく
よくみるサンプルコード そもそもこれって何?
よくみるサンプルコード
͍͢͝Ͱ͔͍ΦϒδΣΫτ͕දࣔ͞ΕΔʜ
オブジェクトがよくわからな い…
まずは外部ツールでASTを見てみる • TypeScript AST Viewer (https://ts-ast-viewer.com/) を使えばどうい うASTに変換されるかわかる • 実際に型を解析する前にこのサイトで解析したい型がどういうASTかを
見てみるとよい
まずは外部ツールでASTを見てみる
ちなみに… • ts.Nodeにはkindというフィールドがあり、ここにASTの種類を表す 値(数字)がありますが300種類以上あります→ • あくまで実装時のデバッグに使う、くらいの気持ちで いるほうがいいかも
ちなみに…
それぞれのkindにあわせてparse • 先程のnode.kindを使って解析してみると…
None
Kindだけでは(型的に)絞り込めない • kindだけではASTの種別を型的に識別できない • (残念ながら)Disciminated Unionではない • TypeAliasDeclaration型の継承関係を見てみるとこんなに複雑 TypeAliasDeclaration DeclarationState
LocalsContainer JSDocContainer NamedDeclaration Statement Declaration Node
isXXXを使う • isXXXを使うとに単true/falseを返すだけでなく型ガードにより 推論してくれる • 今回だとisTypeAliasDeclaration
isXXXを使う • isXXXを使うとに単true/falseを返すだけでなく型ガードにより 推論してくれる • 今回だとisTypeAliasDeclaration
実際にHTML型をparseしていく
軽くhtml-typeの型について • <html>、<body>、<div>、<p>について型として定義されている • HTMLとして子要素に持てるもの(HTMLNodeとする)は • 文字列(Text) • HTMLNode •
HTMLNodeあるいはTextを含むリスト • 例えば<html>タグは右のように定義されている
None
• 以下のようなシンプルなASTをパースしていく まずはシンプルなケースでパースしていく
• SourceFile自体がファイルのrootに位置するのでそこから深さ優先で 再帰処理していく 頭にあるSourceFileを識別 SourceFile
• TypeAliasにヒットすると次は型の内部を見ていく TypeAliasを識別 TypeAlias
• 内部構造見てみるとIdentifierとTypeReferenceを持つ TypeAliasを識別 TypeReference Identifier
• node.type.typneNameがIdentifier TypeAliasを識別 TypeReference Identifier
• getTypeAtLocation、typeToTypeNode を使ってちゃんとTypeReferenceを 解析していく TypeReferenceを解析していく
むずかしい
前提知識 • よく出てくる登場人物はこれ • Type: 型システムが扱う型そのもの(ASTではない) • Node: AST Node全般
• TypeNode: 型に関するNode(Nodeを継承している)
前提知識 • NodeからTypeの情報が欲しいとき • TypeからそのNodeの情報が欲しいとき
• getTypeAtLocation、typeToTypeNode を使ってちゃんとTypeReferenceを 解析していく • typeToTypeNodeの第三引数が かなり大事!(一旦undefinedで) • TypeReferenceだと中の型情報が 取り出せない!
TypeReferenceを解析していく
• node.membersでそれぞれの構成要素を取得できる ひたすらTypeLiteralNodeを解析していく Identifier PropertySignature
None
ひたすらTypeLiteralNodeを解析していく • node.membersでそれぞれの構成要素を取得できる • Indentifier: 型名が取得できる • PropertySignature: 今回の場合は Genericsの部分
Identifier PropertySignature
PropertySignatureを解析する前に…
軽くhtml-typeの型について(再掲) • <html>、<body>、<div>、<p>について型として定義されている • HTMLとして子要素に持てるもの(HTMLNodeとする)は • 文字列(Text) • HTMLNode •
HTMLNodeあるいはTextを含むリスト • 例えば<html>タグは右のように定義されている
PropertySignatureをパース • PropertySignatureは3種類ありうる • 文字列リテラル: StringLiteral • タプル(配列): TupleTypeNode •
Typeリテラル: TypeLiteralNode
• PropertySignatureは3種類ありうる • 文字列リテラル: StringLiteral • タプル(配列): TupleTypeNode • Typeリテラル:
TypeLiteralNode PropertySignatureをパース
• 文字列リテラル: StringLiteral • そのまま文字列を取り出す PropertySignatureをパース StringLiteral
• タプル(配列): TupleTypeNode • タプル内要素を再度解析する PropertySignatureをパース TupleTypeNode
• Typeリテラル: TypeLiteralNode • 要素が1つだけなのでその要素を再度解析 PropertySignatureをパース TypeLiteralNode
• PropertySignatureは3種類ありうる • 文字列リテラル: StringLiteral • そのまま文字列を取り出す • タプル(配列): TupleTypeNode
• タプル内要素を再度解析する • Typeリテラル: TypeLiteralNode • 要素が1つだけなのでその要素を再度解析 PropertySignatureをパース StringLiteral TupleTypeNode TypeLiteralNode
実際のASTの構造
生成ができた
より複雑なものをparseしてみる • もっと複雑なものをparseしてみると…
• ここは本来、TypeLiteral>TypeLiteral>StringLiteralとはいるはず… 何が起こっているかを見てみる
何が起こっているかを見てみる
• ここは本来、TypeLiteral>TypeLiteral>StringLiteralとはいるはず… 何が起こっているかを見てみる どうやら省略されてそう…
型解析は不可能
と言う前に…
Configでいじれるところを見る • 今までのコードの中で、何かしらConfigを 入れられそうなのは右の5つ
None
None
None
None
None
TypeToTypeNodeを見てみると… • 今までundefinedで誤魔化してましたが第三引数が大事 • NodeBuildingFlagsにいろいろなフラグがある
None
NodeBuildingFlagsを使う • 今回はTruncate(…)を防ぎたいのでNoTruncateを使う • シフト演算でそれぞれのフラグ定義されているので、 複数のフラグも指定可能!
ちゃんとTruncationが治った!
ここだけの話 • Truncateが出たときにClaude Codeに相談したところ、 すごい力技でTruncateした部分を解消しようとしてました… • ↑まだTypeScript CompilerのことはClaudeもわからない? あるいはそれくらい情報が少ない?
まとめ • まずはAST ViewerでASTを見てみるべし • Compiler Optionは要注意 • TypeScript自体のコードを見るべし •
GitHubじゃなくて手元にClone!(GitHubだと多分みれない…)
良い型ライフを!