0Pricing
TypeScript Academy · レッスン

翻訳キーの型付け

メッセージファイルから有効なキーのユニオンを導出します。

「翻訳キーの型付け」はCoddyKit上の無料TypeScript Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはTypeScript Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 TypeScript Academyコースには全4レッスンが含まれています。

型安全な翻訳キーを導出する

型安全な i18n の構成では、有効な翻訳キーの集合がmessages オブジェクトから導出されます。t("greetng") のようなタイプミスは、ランタイムで文字列が見つからないバグではなく、コンパイルエラーになります。

Messages オブジェクト

まず、メッセージのプレーンなオブジェクトを用意します。これは値とキーの型の両方に対する単一の信頼できる情報源になります。

const messages = {
  greeting: "Hello",
  farewell: "Goodbye",
  cart: {
    empty: "Your cart is empty",
    checkout: "Proceed to checkout",
  },
} as const;

keyof によるトップレベルキー

フラットなオブジェクトでは、keyof typeof によってキーのユニオン型を取得できます。

type TopKeys = keyof typeof messages;
// "greeting" | "farewell" | "cart"

ネストされた場合の問題

実際のメッセージカタログはネストされています。トップレベルのキーだけでなく、"cart.empty" のようなドット区切りのパスが必要です。オブジェクトを走査する再帰的な型が必要になります。

再帰的なパス型

条件型と mapped type を組み合わせると、ネストされたオブジェクトを再帰的にたどり、キーに接頭辞を付けることでドット区切りのパスを構築できます。接頭辞にはテンプレートリテラル型を使います。実際のコードではバッククォートで [backtick]${K}.${Paths<T[K]>}[backtick] と記述します。リストを見やすくするため、ここでは JoinPath(K, child) として示します。

type JoinPath<K extends string, Rest extends string> = K + "." + Rest;
// (Real TS uses a template literal type:
//   the K dot Rest pattern delimited by backticks.)

type Paths<T> = {
  [K in keyof T & string]: T[K] extends string
    ? K
    : K | (T[K] extends object
        ? JoinPath<K, Paths<T[K]>>
        : never);
}[keyof T & string];

適用する

messages の型を Paths に渡すと、有効なドット区切りキーをすべて取得できます。

type MsgKey = Paths<typeof messages>;
// "greeting" | "farewell" | "cart" | "cart.empty" | "cart.checkout"

リーフだけのキー

多くの場合、途中のオブジェクトを除外し、リーフのパス(実際の文字列)だけが必要です。文字列以外のノードをスキップするように再帰処理を調整します。K-dot-child の接頭辞にもテンプレートリテラル型を使います(実際のコードではバッククォートで囲みます)。ここでは JoinPath として示します。

type LeafPaths<T> = T extends string
  ? ""
  : {
      [K in keyof T & string]: T[K] extends string
        ? K
        : JoinPath<K, LeafPaths<T[K]>>;
    }[keyof T & string];
// "greeting" | "farewell" | "cart.empty" | "cart.checkout"

型付き t 関数

導出したキーのユニオン型に翻訳関数のパラメーターを制約し、有効なキーだけがコンパイルを通るようにします。

declare function t(key: LeafPaths<typeof messages>): string;

t("cart.empty");   // ok
t("cart.missing"); // Error: not a valid key

値の型を解決する

さらに、パスから値の型を推論することもできます。これは、メッセージに文字列以外のエントリがある場合に便利です。パスをインデックスとして使う条件型がドットをたどります。パスはテンプレートリテラルパターンで分割します。実際のコードでは、バッククォートを使って Head dot Rest の形と infer を記述します。ここではその分割を SplitHead/SplitRest と表します。

// Conceptually: split "cart.empty" into Head="cart", Rest="empty"
// using a template literal pattern with infer.

type ValueAt<T, P extends string> =
  P extends SplitHead<infer Head, infer Rest>
    ? Head extends keyof T ? ValueAt<T[Head], Rest> : never
    : P extends keyof T ? T[P] : never;

as const が重要な理由

as const がないと、文字列値は string 型に拡大され、後で必要になるリテラル情報(たとえばプレースホルダーの抽出)を失います。カタログは必ず const として宣言してください。

重要な理由

カタログからキーを導出すると、メッセージの追加、名前変更、削除が、許可されるキー型に即座に反映されます。維持する中央の enum も、古くなった文字列定数も必要ありません。

理解度チェック

翻訳キーの型付けについて、理解度を確認しましょう。

まとめ

keyof と再帰的なテンプレートリテラルパス型を使い、messages オブジェクトから有効なキーのユニオン型を導出しました。リーフだけの派生型では途中のオブジェクトを除外でき、型付きの t は無効なキーを拒否します。また、as const によって型の仕組みに必要なリテラル情報が保持されます。

よくある質問

「翻訳キーの型付け」レッスンは無料ですか?

はい。「翻訳キーの型付け」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、TypeScript Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 TypeScript Academyコースには全4レッスンが含まれています。

「翻訳キーの型付け」で何を学びますか?

メッセージファイルから有効なキーのユニオンを導出します。 ブラウザで直接実行するハンズオンコードでTypeScript Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

TypeScript Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのTypeScript Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「翻訳キーの型付け」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このTypeScript Academyレッスンでコードを書いて実行できますか?

はい。すべてのTypeScript Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 翻訳キーの型付け
  2. 補間の型安全性
  3. 型による複数形処理
  4. ロケール対応の型推論
← TypeScript Academyに戻る