Menu

TypeScriptのテンプレートリテラル型: 構文と例

テンプレートリテラル型は、JavaScriptのテンプレート文字列と同じバッククォートの構文で文字列リテラル型を作ります: on${Capitalize<E>}。構文、ユニオン型が掛け合わされる仕組み、Uppercase と Capitalize、${number}px のようなパターン、マップ型での getter、infer による文字列の解析を解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

テンプレートリテラル型は、JavaScriptのテンプレート文字列と同じバッククォートの構文で文字列リテラル型を作ります。`on${Capitalize<"click" | "focus">}` は、コンパイラーが計算した型 "onClick" | "onFocus" です:

テンプレートリテラル型はコンパイル時にしか存在しません。コードを書いている間に文字列リテラルと型付きの値をチェックし、JavaScriptの出力には何も加えません。上のハンドラーの表は、名前ごとに1つの関数を要求するために Record を使っています。

構文

バッククォートの中には、リテラルのテキストと ${...} のプレースホルダーを書きます。プレースホルダーに入るのは値ではなく型です。文字列、数値、bigint、boolean のリテラル型、それらのユニオン型、または広い型 string、number、bigint、boolean、null、undefined のどれかです。

string や number のような広い型のプレースホルダーはパターンを作ります。型は `hello ${string}` のまま残り、一致する任意の文字列を受け付けます。boolean のような有限のユニオン型のプレースホルダーは、メンバーに展開されます。

ユニオン型は掛け合わされる

ユニオン型が複数あると、結果はすべての組み合わせになります:

3つのサイズと2つのトーンで6つのメンバーになります。数はすぐに増えます。それぞれ10文字のユニオン型を持つプレースホルダーが5つあれば100,000個のメンバーになり、TypeScriptは error TS2590: Expression produces a union type that is too complex to represent で拒否します。すべての正確な値が必要でなければ、${string} のような広いプレースホルダーを使いましょう。

Uppercase、Lowercase、Capitalize、Uncapitalize

4つの組み込みの型が、文字列リテラル型の大文字と小文字を変えます。これらは組み込み(intrinsic)で、TypeScriptで書かれているのではなく、コンパイラーの中で実装されています。

型入力結果
Uppercase<S>"hello world""HELLO WORLD"
Lowercase<S>"Content-Type""content-type"
Capitalize<S>"hello world""Hello world"
Uncapitalize<S>"UserName""userName"

変わるのは型だけです。対応する実行時の文字列を作るには、やはり toUpperCase() を呼ぶか、自分で切り出して先頭を大文字にし、結果が正確な型を持つことをTypeScriptに伝えます:

toUpperCase() は普通の string を返すと型付けされているので、as が必要です。呼び出し側に見えるのは関数のシグネチャなので、capitalize("report") はリテラル型 "Report" になります。

文字列のパターン: ${number}px など

パターンの型は、決まった形のすべての文字列を受け付けます。CSSの値や、決まった接頭辞を持つ ID やキーに便利です:

${number} は、JavaScriptが数値として読む任意の文字列を受け付けるので、見た目よりゆるくなっています。"-3px"、"1e3px"、"0x10px" はどれも型チェックを通ります。こうしたパターンは完全な検証ではなく、リテラルの打ち間違いを防ぐものとして扱いましょう。

マップ型と組み合わせたテンプレートリテラル

テンプレートリテラル型がいちばん役立つのは、マップ型の as 句として、あるプロパティ名から別のプロパティ名を生成するときです:

Capitalize は数値やシンボルを受け付けないので、string & K で文字列のキーだけを残しています。各コールバックは、監視するプロパティから引数の型を得ます。

infer で文字列を解析する

条件型の中では、テンプレートリテラルで文字列と照合し、infer でその一部を捉えられます。次の例はルートのパターンからパラメーター名を取り出します:

呼び出しで postId を省くと、コンパイラーが足りないと報告します。

テンプレート式は string に広がる

普通のコードのテンプレート文字列の式は、すべての部分がリテラル型であっても string と型付けされます。テンプレートリテラル型を保つには as const を付けます:

as const がなければ、loose を `log:${Level}` に代入すると失敗します。string は何でもありうるからです。

よくある質問

TypeScriptのテンプレートリテラル型とは何ですか?

バッククォートと ${...} のプレースホルダーで書く文字列リテラル型で、JavaScriptのテンプレート文字列を型のレベルで使うようなものです。type Greeting = `hello ${string}` は hello で始まる任意の文字列を受け付け、`on${Capitalize<"click">}` はリテラル型 "onClick" です。

テンプレートリテラル型にユニオン型を入れるとどうなりますか?

テンプレートはメンバーごとに展開され、ユニオン型が複数あればすべての組み合わせが得られます。`${"sm" | "lg"}-${"red" | "blue"}` は "sm-red" | "sm-blue" | "lg-red" | "lg-blue" です。組み合わせが非常に多いと、エラー TS2590 で失敗します。

Uppercase、Lowercase、Capitalize、Uncapitalize は何をしますか?

文字列リテラル型を変換する組み込みの型です。Uppercase<"id"> は "ID"、Lowercase<"ID"> は "id"、Capitalize<"name"> は "Name"、Uncapitalize<"Name"> は "name" です。変わるのは型だけで、実行時の文字列を変えるにはやはり toUpperCase() などを呼びます。

テンプレートリテラル型は実行時に文字列を検証しますか?

しません。ほかのTypeScriptの型と同じく消去されるので、コンパイル時に文字列リテラルと型付きの値をチェックするだけです。JSONやユーザー入力から実行時に届く文字列は、自分のコードでチェックするまではただの string です。

テンプレート文字列がリテラル型ではなく string と型付けされるのはなぜですか?

`on${event}` のようなテンプレート式は、変数に代入すると string に広げられます。as const(`on${event}` as const)を付けるか、代入先の型に注釈を付ければ、TypeScriptは "onclick" | "onfocus" のようなテンプレートリテラル型を保ちます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める