Slide 1

Slide 1 text

TypeScriptはどのようにどこまで推論できるのか ─ とにかく as は禁止で 2026/05/23:TSKaigi 2026 (Sponsor Session) ypresto (@yuya_presto / プレスト)

Slide 2

Slide 2 text

ypresto © LayerX Inc. LayerX バクラク事業部 (2024-01〜) プロダクト開発部 債権債務チーム Software Engineer "フロントエンド" の基盤もよりよくしてます 2

Slide 3

Slide 3 text

話をしよう (型推論の) あれは5万行のchecker.ts・・・ いや、今は3万行のchecker.goだったか checker.tsのコードはついに分解されました © LayerX Inc. 3

Slide 4

Slide 4 text

TypeScriptの強力な型推論と安全性について を紹介しようと思います! © LayerX Inc. Contextual Type 型パラメータ推論 制御フロー解析 asは全部禁止するべきか???? (as const除く) 4

Slide 5

Slide 5 text

型の決定は内側 → 外側へ impl: checkExpression → checkCallExpression → callee/receiver を先に解決 → getReturnTypeOfSignature © LayerX Inc. 5

Slide 6

Slide 6 text

逆方向:Contextual Type impl: ① arrow fn 解決→getContextualType: checkFunctionExpressionOrObjectLiteralMethod → contextuallyCheck… → getContextualSignature → getApparentTypeOfContextualType → getContextualType / ② 親をたどる: getContextualTypeForJsxAttribute / ③ e へ降ろす: assignContextualParameterTypes(lazy: getContextuallyTypedParameterType) © LayerX Inc. 6

Slide 7

Slide 7 text

当たり前に通るけど、Contextual Typeのおかげ impl: 型引数を contextual から: inferTypeArguments(contextual→戻り値型へ推論) / widen 抑制: getWidenedLiteralTypeForInitializer / isLiteralOfContextualType / ちなみに [] は any[] になるようです (never[]じゃなかったっけ...) © LayerX Inc. 7

Slide 8

Slide 8 text

ところで、型パラメータの解決ルール impl: instantiateContextualType (nonFixingMapper で推論途中の T を反映) / inferTypeArguments / 候補集め inferTypes: 引数→param ・ 戻り値 →return(低優先度) / getInferredType(候補が無ければ default / constraint) © LayerX Inc. 下記の優先順でTを推論する 引数: 'click' ← 採用 返り値のContextual Type: 'click' | 'hover' 8

Slide 9

Slide 9 text

型パラメータ推論とNoInfer impl: isNoInferType (= constraint が unknown の substitution type) / 候補集めをブロック (inferFromTypes) / 確定後は素の T に解決 © LayerX Inc. 下記の優先順でTを推論する 引数: NoInferなので対象外 返り値のContextual Type: 'click' | 'hover' ← 採用 9

Slide 10

Slide 10 text

制御フロー解析と型ガード 変数を読む際、その地点から制御フローを遡り、通過した条件・代入から型を再構成する impl: getFlowTypeOfReference / narrowType © LayerX Inc. 10

Slide 11

Slide 11 text

最近は代入やComputed Propertyも絞れるように進化..! impl: narrowType の inline 展開 / isConstantReference / getDiscriminantPropertyAccess / getCandidateDiscriminantPropertyAccess / isMatchingReference © LayerX Inc. 11

Slide 12

Slide 12 text

こんだけ推論が強力なら もうasはいらないのでは!? バクラクmonorepo内のas全部抜いてみた (as constは除きます) © LayerX Inc. 12

Slide 13

Slide 13 text

as?破壊したはずでは?? → 局所的なヘルパ関数だけ許す © LayerX Inc. 13

Slide 14

Slide 14 text

型パズル難しい © LayerX Inc. form.getValues(): Tを返す → covariant 共変 (Tがより大きければ代入できる) form.setValue(): Tを受け取る → contravariant 反変 (Tがより小さければ代入できる) 両方合わせて → invariant: Tは完全に同じでないとダメ 14

Slide 15

Slide 15 text

でも、asの使用箇所は、9割(AI曰く)が 人間やAIが敗北した結果だった..!!! © LayerX Inc. 15

Slide 16

Slide 16 text

早期対策が、あなたを救うのです ヘルパ関数に寄せて、asを原則全部禁止しよう! @typescript-eslint/no-explicit-any no-restricted-syntax © LayerX Inc. 16

Slide 17

Slide 17 text

終 制作・著作 LayerX © LayerX Inc. 17

Slide 18

Slide 18 text

ふろく:適用すべきガードレール 自動で有効になってないことがあるもの

Slide 19

Slide 19 text

Object Literalには必ず型注釈を付ける Excess Property Checkが動かずtypoを見逃す © LayerX Inc. 19

Slide 20

Slide 20 text

"noUncheckedIndexedAccess": true Index Out of Bounds の防止のため、配列のインデックスアクセスの型を T から T | undefined にする © LayerX Inc. 20