それでは、テンプレート リテラル型を使用した TypeScript のコンパイル時メタプログラミングの魅力的な世界に飛び込んでみましょう。この強力な機能により、コードをより安全で表現力豊かにする、非常に優れた型レベルのマジックを作成できるようになります。
まず、テンプレート リテラル タイプとは正確には何ですか?これらは、文字列リテラルに基づいて新しい型を操作および作成する方法です。それは、自分のタイプ専用のミニプログラミング言語を持つようなものです。かなりきれいですね?
簡単な例から始めましょう:
type Greeting<T extends string> = `Hello, ${T}!`; type Result = Greeting<"World">; // "Hello, World!"
ここでは、文字列を取得して挨拶でラップする型を作成しました。 TypeScript コンパイラは、コンパイル時に結果の型を判断します。ただし、これは表面をなぞっただけです。
テンプレート リテラル タイプを使用して、より複雑な変換を作成できます。たとえば、snake_case を CamelCase に変換する型を作成したいとします:
type SnakeToCamel<S extends string> = S extends `${infer T}_${infer U}` ? `${T}${Capitalize<SnakeToCamel<U>>}` : S; type Result = SnakeToCamel<"hello_world_typescript">; // "helloWorldTypescript"
このタイプは、入力文字列を再帰的に変換し、アンダースコアの後の各部分を大文字にします。ここでは infer キーワードが重要です。これにより、文字列の一部を新しい型の変数に抽出できるようになります。
しかし、なぜそこで止まるのでしょうか?これらの手法を使用して、型システム内にドメイン固有言語 (DSL) 全体を構築できます。タイプセーフな SQL クエリ ビルダーを作成することを想像してください:
type Table = "users" | "posts" | "comments"; type Column = "id" | "name" | "email" | "content"; type Select<T extends Table, C extends Column> = `SELECT ${C} FROM ${T}`; type Where<T extends string> = `WHERE ${T}`; type Query<T extends Table, C extends Column, W extends string> = `${Select<T, C>} ${Where<W>}`; type UserQuery = Query<"users", "name" | "email", "id = 1">; // "SELECT name, email FROM users WHERE id = 1"
この設定により、有効なテーブルから有効な列のみが選択され、コンパイル時にすべてチェックされることが保証されます。列名の入力ミスによる実行時エラーはもう発生しません!
より複雑な型レベルの計算を実装することで、これをさらに進めることができます。基本的な算術演算を実行できる型を作成しましょう:
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; type AddDigits<A extends Digit, B extends Digit> = // ... (implementation details omitted for brevity) type Add<A extends string, B extends string> = // ... (implementation details omitted for brevity) type Result = Add<"123", "456">; // "579"
この型は、文字列として表される 2 つの数値を加算できます。実際の実装は非常に複雑で、多くの条件型と再帰が含まれますが、最終結果は純粋なコンパイル時の魔法です。
これらの技術の実際的な応用例の 1 つは、高度なフォーム検証スキーマの作成です。フォームの形状を記述するタイプを定義し、それを使用して検証ルールを生成できます。
type Form = { name: string; email: string; age: number; }; type ValidationRule<T> = T extends string ? "isString" : T extends number ? "isNumber" : never; type ValidationSchema<T> = { [K in keyof T]: ValidationRule<T[K]>; }; type FormValidation = ValidationSchema<Form>; // { name: "isString", email: "isString", age: "isNumber" }
このスキーマを使用して実行時検証コードを生成し、検証ロジックが常に型定義と一致することを保証します。
テンプレート リテラル型を使用すると、より柔軟な API を作成することもできます。これらを使用して、適切な型推論によるメソッド チェーンを実装できます。
type Chainable<T> = { set: <K extends string, V>(key: K, value: V) => Chainable<T & { [P in K]: V }>; get: () => T; }; declare function createChainable<T>(): Chainable<T>; const result = createChainable() .set("foo", 123) .set("bar", "hello") .get(); // result type: { foo: number, bar: string }
このパターンにより、型システムが各ステップで蓄積されたプロパティを追跡しながら、オブジェクトを段階的に構築することができます。
コンパイル時メタプログラミングの最も強力な側面の 1 つは、既存の型に基づいて新しい型を生成できることです。これを使用して、他の型を便利な方法で変換するユーティリティ型を作成できます。たとえば、オブジェクトのすべてのプロパティをオプションにする型を作成してみましょう。ただし、最初のレベルのみです。
type Greeting<T extends string> = `Hello, ${T}!`; type Result = Greeting<"World">; // "Hello, World!"
このタイプでは、トップレベルのプロパティがオプションになりますが、ネストされたオブジェクトは変更されません。これは、TypeScript の組み込み Partial 型のより微妙なバージョンです。
テンプレート リテラル タイプを使用して、より表現力豊かなエラー メッセージを作成することもできます。不可解な型エラーが発生する代わりに、開発者を正確な問題に導くことができます。
type SnakeToCamel<S extends string> = S extends `${infer T}_${infer U}` ? `${T}${Capitalize<SnakeToCamel<U>>}` : S; type Result = SnakeToCamel<"hello_world_typescript">; // "helloWorldTypescript"
この手法は、ユーザーに明確なフィードバックを提供することが重要であるライブラリ開発で特に役立ちます。
もう 1 つの興味深い応用例は、タイプセーフなイベント エミッターの作成です。テンプレート リテラル タイプを使用して、イベント名とそれに対応するペイロードが正しく一致していることを確認できます。
type Table = "users" | "posts" | "comments"; type Column = "id" | "name" | "email" | "content"; type Select<T extends Table, C extends Column> = `SELECT ${C} FROM ${T}`; type Where<T extends string> = `WHERE ${T}`; type Query<T extends Table, C extends Column, W extends string> = `${Select<T, C>} ${Where<W>}`; type UserQuery = Query<"users", "name" | "email", "id = 1">; // "SELECT name, email FROM users WHERE id = 1"
この設定により、常に正しいペイロード タイプでイベントを発行し、リッスンすることが保証されます。
テンプレート リテラル型は、型レベルのステート マシンの実装にも使用できます。これは、複雑なワークフローやプロトコルをモデル化する場合に非常に役立ちます:
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; type AddDigits<A extends Digit, B extends Digit> = // ... (implementation details omitted for brevity) type Add<A extends string, B extends string> = // ... (implementation details omitted for brevity) type Result = Add<"123", "456">; // "579"
このステート マシンは完全にタイプセーフです。無効な遷移は許可されず、現在の状態を正確に追跡します。
結論として、TypeScript のテンプレート リテラル型を使用したコンパイル時メタプログラミングは可能性の世界を開きます。これにより、より表現力豊かで、タイプセーフで、自己文書化されたコードを作成できます。エラーを早期に検出し、開発者エクスペリエンスを向上させ、型に基づいてコードを生成することもできます。これらの手法は複雑になる可能性がありますが、堅牢で柔軟なシステムを構築するための強力なツールを提供します。他の高度な機能と同様に、それらを慎重に使用することが重要です。場合によっては、よりシンプルなソリューションの方が保守しやすい場合があります。しかし、コンパイル時のメタプログラミングをうまく利用すると、TypeScript コードの品質と信頼性を大幅に向上させることができます。
私たちの作品をぜひチェックしてください:
インベスターセントラル | スマートな暮らし | エポックとエコー | 不可解な謎 | ヒンドゥーヴァ | エリート開発者 | JS スクール
Tech Koala Insights | エポックズ&エコーズワールド | インベスター・セントラル・メディア | 不可解な謎 中 | 科学とエポックミディアム | 現代ヒンドゥーヴァ
以上がTypeScript のテンプレート リテラル型をマスターする: コードの安全性と表現力を向上するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。