Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
型情報を手繰り寄せる技術~TypeScript Compiler APIによる型解析実践~
Search
jiko21
November 23, 2025
0
780
型情報を手繰り寄せる技術~TypeScript Compiler APIによる型解析実践~
TSKaigi Hokurikuの資料です
jiko21
November 23, 2025
Tweet
Share
More Decks by jiko21
See All by jiko21
Creating a Next.js-style Framework with Bun and Hono
jiko21
0
160
Array Grouping will soon be arriving at TypeScript
jiko21
0
130
Copying Array Methods arrived at TypeScript
jiko21
1
700
SSRで動的に OGP画像を生成したい! 〜Cloudflare Workersから@vercel/og移行編〜
jiko21
0
140
node:test will replace Jest?
jiko21
0
97
どこでも動かすために… TypeScriptでライブラリ開発の すゝめ
jiko21
2
390
NestJS a progressive web framework
jiko21
3
2.2k
レガシーなフロントエンドをリプレイスする
jiko21
5
1.6k
Deep Dive Into Vue Composition API
jiko21
0
3.2k
Featured
See All Featured
Fireside Chat
paigeccino
41
3.7k
Designing Experiences People Love
moore
143
24k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
10
710
Embracing the Ebb and Flow
colly
88
4.9k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
359
30k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
7.8k
YesSQL, Process and Tooling at Scale
rocio
174
15k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
140
34k
Scaling GitHub
holman
464
140k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
132
19k
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だと多分みれない…)
良い型ライフを!