はじめに
TypeScriptは、静的型付け言語の中でも非常に柔軟で強力な「型システム」を備えています。
その中でも TypeScript 4.1 で導入された Template Literal Types(テンプレートリテラル型) は、文字列の組み合わせルールを「型」として定義できる画期的な機能です。
JavaScriptでおなじみのテンプレートリテラル構文(`hello ${name}`)の仕組みをそのまま型定義に持ち込むことで、型安全なCSSクラス名、APIのパス解決、デザインシステムのレイアウト指定などを実現できます。本記事では、このテンプレートリテラル型の基本と実践的なパターンを紹介します。
1. テンプレートリテラル型の基本
テンプレートリテラル型は、文字列型のプレースホルダーにユニオン型を流し込むことで、自動的にすべての組み合わせ(総当たり)の文字列パターンを型として生成してくれます。
最もシンプルな例
type Align = "left" | "center" | "right";
type Valign = "top" | "middle" | "bottom";
// 縦と横の組み合わせの配置ルールを定義する
// 自動的に "left-top" | "left-middle" | ... | "right-bottom" の全9パターンが生成される
type Position = `${Align}-${Valign}`;
// OK
const pos1: Position = "left-top";
// エラー!"center-center" は存在しません
const pos2: Position = "center-center";
これまでは同様の型を作るために、すべての組み合わせを手動でタイピングしてユニオン型を作る必要がありましたが、テンプレートリテラル型によって一瞬で定義可能になります。
2. 実践的なユースケース
① デザインシステムにおけるプレフィックス付きCSSクラス名
コンポーネントライブラリを作る際、指定されたスタイルバリアントとサイズに基づいて、安全なBEM風のクラス名のみを許可するパターンです。
type Size = "sm" | "md" | "lg";
type Variant = "primary" | "secondary" | "danger";
// btn-sm-primary, btn-lg-danger などの命名ルールを型定義
type ButtonClassName = `btn-${Size}-${Variant}`;
function getButtonClass(size: Size, variant: Variant): ButtonClassName {
return `btn-${size}-${variant}`;
}
// OK
const className = getButtonClass("sm", "primary"); // btn-sm-primary
// エラー!不正な値はガードされる
const invalidClass: ButtonClassName = "btn-xl-primary";
② APIのホストとエンドポイントの結合
外部APIを呼び出す際、URLのスキーマ(http または https)とホスト名を厳格に組み合わせる定義です。
type Protocol = "http" | "https";
type Domain = "api.example.com" | "staging.example.com";
type ApiEndpoint = `${Protocol}://${Domain}/${string}`;
// OK
const endpoint1: ApiEndpoint = "https://api.example.com/users";
// エラー!プロトコルが不正
const endpoint2: ApiEndpoint = "ftp://api.example.com/users";
末尾の ${string} パラメータにより、指定ドメインから始まる任意の文字列URLであることを型システムが検知してくれます。
3. ユーティリティ型との組み合わせ(大文字・小文字変換)
TypeScriptは、テンプレートリテラル型で処理する文字列を変更するための特別な組み込み型を提供しています。
Uppercase<S>: すべて大文字に変換Lowercase<S>: すべて小文字に変換Capitalize<S>: 先頭の文字のみ大文字に変換Uncapitalize<S>: 先頭の文字のみ小文字に変換
これらを組み合わせることで、プロパティの「Getter/Setter」のメソッド名を型で自動生成することができます。
type UserFields = "name" | "email";
// 各フィールドに対するGetterメソッド名を定義する
// "getName" | "getEmail" が自動生成される
type GetterNames = `get${Capitalize<UserFields>}`;
const userGetters: Record<GetterNames, () => string> = {
getName: () => "Takao",
getEmail: () => "[email protected]"
};
まとめ
TypeScriptのテンプレートリテラル型は、文字列に型安全な「文法ルール」を与えるための非常に強力なアプローチです。
- ユニオン型をプレースホルダーに埋め込み、すべての組み合わせパターンを型として自動生成する
${string}を使って、特定の接頭辞(Prefix)や接尾辞(Suffix)を持つ文字列のみを許可するCapitalizeなどの組み込みヘルパーを使い、キャメルケースのメソッド名などを自動定義する
このテクニックを活用することで、タイプミスによるバグをコンパイル段階で完全に防ぐことができます。ぜひ設計に取り入れてみてください。
