1. ホーム
  2. TypeScript

【TypeScript】Template Literal Types(テンプレートリテラル型)の使い方|文字列の型を組み立てる

Share

TypeScript には、文字列そのものを組み立てるのではなく、文字列の「型」を組み立てる仕組みがあります。それが Template Literal Types(テンプレートリテラル型)です。JavaScript のテンプレートリテラル(バッククォートで囲む文字列)とよく似た書き方で、「必ず Hello で始まる文字列だけを受け付ける型」や「top-leftbottom-right のような組み合わせだけを許す型」を定義できます。この記事では、基本の書き方からユニオン型との組み合わせ、組み込みの文字列操作型、そして実務での使いどころまで、初心者〜中級者向けに解説します。TypeScript 4.1 以降で使える機能です。

これは「実行時の文字列」ではなく「型」の機能です

最初に大事な前置きをします。JavaScript のテンプレートリテラル(例: `Hello ${name}`)は、実際に動くプログラムの中で文字列という「値」を作るものです。一方この記事で扱う Template Literal Types は、同じような見た目でも「型」を作る機能で、コンパイル時(型チェックのとき)にしか存在しません。JavaScript に変換された時点で消えてしまうため、実行時には何の影響もありません。「文字列を生成する」のではなく「どんな文字列を受け付けるかを決める」もの、と考えてください。

基本の書き方:バッククォートと ${} で型を組み立てる

テンプレートリテラル型は、バッククォート ` で囲み、その中に ${} で別の型を埋め込んで書きます。次の例では、「Hello のあとに任意の文字列が続く」という形の型を定義しています。

greeting.ts
// "Hello " で始まる文字列だけを受け付ける型
type Greeting = `Hello ${string}`;

const a: Greeting = "Hello 太郎";   // OK
const b: Greeting = "Hello world"; // OK
const c: Greeting = "Hi 太郎";      // エラー:"Hello " で始まっていない

${string} の部分は「ここには任意の文字列が入る」という意味です。string のほかに numberboolean なども埋め込めます。たとえば `id-${number}` とすれば、"id-1""id-42" のような文字列だけを許す型になります。文字列リテラル型("Hello" のような特定の文字列だけを表す型)を ${} に入れれば、その文字列がそのまま埋め込まれます。

ユニオン型と組み合わせると全パターンに展開される

テンプレートリテラル型の真価は、ユニオン型"a" | "b" のように「どれか」を表す型)と組み合わせたときに現れます。${} にユニオン型を入れると、その全組み合わせが自動的に展開されます。

position.ts
type Vertical = "top" | "bottom";
type Horizontal = "left" | "right";

// 2 × 2 = 4 通りに展開される
type Position = `${Vertical}-${Horizontal}`;
// 実際の型は次と同じ:
// "top-left" | "top-right" | "bottom-left" | "bottom-right"

const p1: Position = "top-left";     // OK
const p2: Position = "bottom-right"; // OK
const p3: Position = "center";       // エラー:4 通りのどれでもない

Vertical が2通り、Horizontal が2通りなので、掛け合わせて4通りの文字列型が生まれます。複数の ${} にユニオン型を入れると、それぞれの掛け算の数だけパターンが展開されるわけです。手で "top-left" | "top-right" | ... と書き並べる必要がなくなり、要素を追加したときも自動で組み合わせが増えるのが利点です。

組み込みの文字列操作型(Uppercase など)

TypeScript には、文字列リテラル型の大文字・小文字を変換するための組み込みの型が4つ用意されています。これらはテンプレートリテラル型と一緒に使うことが多く、型の中で文字列を加工できます。

説明
Uppercase<T>すべての文字を大文字にする("abc""ABC"
Lowercase<T>すべての文字を小文字にする("ABC""abc"
Capitalize<T>先頭の1文字だけ大文字にする("abc""Abc"
Uncapitalize<T>先頭の1文字だけ小文字にする("Abc""abc"

使い方は、対象の文字列型を <> の中に渡すだけです。次の例で挙動を確認してみましょう。

string-manip.ts
type A = Uppercase<"hello">;      // "HELLO"
type B = Lowercase<"HELLO">;      // "hello"
type C = Capitalize<"click">;     // "Click"
type D = Uncapitalize<"Click">;   // "click"

// テンプレートリテラル型の中でも使える
type Shout = `${Uppercase<"warning">}!`; // "WARNING!"

特に Capitalize は、次で紹介するイベント名の生成のように「先頭を大文字にしてつなげる」場面でよく使います。

実務での使いどころ

CSS の px サイズを表す型

「数値のあとに必ず px が付く文字列」を型で表現できます。単位の付け忘れや誤ったフォーマットを、コンパイル時に防げます。

px-size.ts
type PxSize = `${number}px`;

const width: PxSize = "16px";  // OK
const gap: PxSize = "8px";     // OK
const bad: PxSize = "16";      // エラー:"px" が付いていない
const bad2: PxSize = "16rem";  // エラー:単位が px ではない

イベント名を on + Capitalize で作る

React のように「clickonClick」というイベントハンドラ名を扱う場面では、Capitalize と組み合わせて型を組み立てられます。元のイベント名の一覧から、対応する on〜 の名前を自動生成できます。

event-name.ts
type EventName = "click" | "focus" | "change";

// "on" + 先頭を大文字にしたイベント名
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onChange"

const handler: HandlerName = "onClick";  // OK
const wrong: HandlerName = "onclick";    // エラー:C が小文字

API のパスを表す型

API のエンドポイントを「必ず /api/ で始まるパス」として型にしておくと、タイプミスや誤ったパスの指定を防げます。

api-path.ts
type ApiPath = `/api/${string}`;

function get(path: ApiPath) {
  // 省略:fetch などでパスを使う
}

get("/api/users");        // OK
get("/api/posts/1");      // OK
get("/users");            // エラー:"/api/" で始まっていない

keyof や Mapped Types との組み合わせ

一歩進んだ使い方として、オブジェクトのプロパティ名(keyof で取り出せる)を元に、新しいプロパティ名を持つ型を作ることもできます。Mapped Types(既存の型のプロパティを1つずつ変換して新しい型を作る仕組み)の中でテンプレートリテラル型を使うと、たとえば各プロパティに対応する「getter メソッド名」を自動生成できます。

mapped-getters.ts
interface Person {
  name: string;
  age: number;
}

// 各プロパティに対応する getName / getAge を持つ型を生成
type Getters<T> = {
  [K in keyof T & string as `get${Capitalize<K>}`]: () => T[K];
};

type PersonGetters = Getters<Person>;
// {
//   getName: () => string;
//   getAge: () => number;
// }

[K in keyof T & string as ...]as の部分でキー名を作り替えているのがポイントです。keyof T だけだと数値や記号のキーも含まれ得るため、& string で文字列のキーに絞ってから Capitalize に渡しています。ここは少し複雑なので、最初は「テンプレートリテラル型はこういう応用にも使える」という程度に押さえておけば十分です。

まとめ

Template Literal Types は、バッククォートと ${} を使って文字列の型を組み立てる機能です。`Hello ${string}` のように形を決めたり、ユニオン型と組み合わせて "top-left" | "top-right" | ... のような全組み合わせを自動展開したりできます。Uppercase / Lowercase / Capitalize / Uncapitalize の4つの組み込み型を使えば、型の中で文字列を大文字・小文字に加工できます。CSS の px サイズ、on${Capitalize<T>} のイベント名、API のパスなど、「決まった形の文字列」を安全に扱いたい場面で活躍します。あくまでコンパイル時の型チェック機能であり、実行時の文字列を作るものではない点だけ、あらためて意識しておきましょう。TypeScript 4.1 以降で使えます。

参考ページ