テンプレートリテラル型を活用して直積によってユニオンを生成し、infer で文字列リテラルを解析する。
このレッスンを Kodokon で開くテンプレートリテラル型は、テンプレートリテラルの構文を型の世界に持ち込みます。ユニオンを補間すると、コンパイラは直積を計算します: 可能なすべての組み合わせが結果のメンバーになります。これは、イベントキー、ルート、デザインシステムのトークンを 1 文字単位まで正確に型付けできるツールです。
type Lang = "fr" | "en";
type Theme = "light" | "dark";
type ThemeKey = `${Lang}-${Theme}`;
// "fr-light" | "fr-dark" | "en-light" | "en-dark"その真価は infer と組み合わせたときに現れます: テンプレートリテラル型は文字列に対するマッチングパターンになります。コンパイラは各セグメントに一致するよう入力リテラルを切り分けます。複数の分割が可能な場合は、区切り文字の最初の出現が勝ちます。再帰を加えれば、コンパイル時に評価される本格的なパーサーが得られます。
type Split<S extends string, Sep extends string> =
S extends `${infer Head}${Sep}${infer Rest}`
? [Head, ...Split<Rest, Sep>]
: [S];
type Parts = Split<"a.b.c", ".">;
// ["a", "b", "c"]このパターンは、Express スタイルのルートのパラメータを抽出するときに輝きます。各再帰呼び出しが 1 つの :param セグメントを消費し、最終的なユニオンがすべてのパラメータ名を列挙します。それを Record に渡してハンドラのシグネチャを型付けできます。
type Params<Path extends string> =
Path extends `${string}:${infer P}/${infer Rest}`
? P | Params<Rest>
: Path extends `${string}:${infer P}`
? P
: never;
type RouteParams = Params<"/users/:id/posts/:postId">;
// "id" | "postId"TypeScript は 4 つの組み込み (intrinsic) ユーティリティ - Uppercase、Lowercase、Capitalize、Uncapitalize - を提供しており、これらはコンパイラ内部で直接実装されているため(intrinsic キーワード)、自分で書き直すことはできません。もう一つの実運用パターン: string & {}。ユニオンの中では、string がリテラルを吸収してしまい、エディタは補完を失います。{} と交差させると、等価だが別個の型が作られ、コンパイラはそれを縮約しません。
type Method = "get" | "post";
type Handler = `on${Capitalize<Method>}`;
// "onGet" | "onPost"
type KnownColor = "red" | "green" | "blue";
type Color = KnownColor | (string & {});
declare function paint(color: Color): void;
paint("red"); // suggested by autocompletion
paint("#ff8800"); // also acceptedA が 3 メンバー、B が 4 メンバーを持つ場合、${A}-${B} は何メンバーを含みますか?Capitalize をリテラル "hello world" に適用すると何が生成されますか?KnownColor | string ではなく KnownColor | (string & {}) と書くのですか?