【実務・中級編】 インデックスアクセス型とユニオン型の組み合わせ – TypeScript実践ガイド

やあ、現場でゴリゴリにコードを書いてるかい?チーフアーキテクトの俺だ。

今日は、TypeScriptを触り始めてしばらく経った中級者の君たちが、必ず一度は直面する「型の定義が重複して、あっちを直せばこっちが壊れる」という不毛な保守作業から解放されるための、とっておきのテクニックを教えよう。

テーマは「インデックスアクセス型(Indexed Access Types)とユニオン型のマリアージュ」だ。

「`User[‘id’]` で型が取れるのは知ってるよ」だって? 甘いな。その一歩先、ユニオン型を組み合わせて「複数のプロパティの型をガサッと一気に抽出する」手法こそが、大規模開発における「単一ソースの原則(Single Source of Truth)」を守るための生命線なんだ。

さあ、エディタの準備はいいか? 現場の泥臭い知見を詰め込んだ、本物のTypeScriptの世界へ案内しよう。

—

1. なぜ「インデックスアクセス型」にユニオン型を放り込むのか

まず、基本をおさらいしよう。`Type[Key]` という書き方は、特定のオブジェクト型からプロパティの型を引っこ抜く手法だ。

だが、実務では「このオブジェクトの中の、この3つのプロパティのどれかが入る型が欲しい」という場面が多発する。例えば、UIコンポーネントのカラーパレットや、APIレスポンスのステータスコードの定義だ。

ここで初心者は、律儀に新しい `type` を手書きで作ってしまう。

// 悪い例:手書きによる重複(DRYじゃない!)
interface Theme {
primary: string;
secondary: string;
success: string;
error: string;
}

// Themeが変わるたびにここも修正しなきゃいけない…地獄の始まりだ
type ThemeColor = string; // 大雑把すぎるし、これじゃ意味がない

ここでインデックスアクセス型の真価を発揮させる。キーの部分に「ユニオン型(`’primary’ | ‘secondary’`)」を叩き込むんだ。

2. 複数の型を一気に抽出する「Type[K1 | K2]」の魔法

TypeScriptの型システムには、「インデックスにユニオン型を渡すと、結果もユニオン型で返ってくる」という性質がある。これを知っているだけで、君のコードの堅牢性は跳ね上がる。

具体的なコードで見てみよう。

/

  • 実務でよくある「デザインシステムの定義」を想定したサンプル

/

export interface AppConfig {
version: number;
apiEndpoint: string;
retries: number;
theme: ‘light’ | ‘dark’ | ‘system’;
debugMode: boolean;
}

// 【ここがポイント!】
// 複数のキーをユニオン型で指定することで、それに対応する値の型を「一括抽出」できる。
type NetworkSettings = AppConfig[‘apiEndpoint’ | ‘retries’];
// 結果は: string | number となる。

// さらに、keyof と組み合わせると最強の武器になる
type AllValues = AppConfig[keyof AppConfig];
// 結果は: number | string | ‘light’ | ‘dark’ | ‘system’ | boolean となる。

/

  • 現場で即戦力のTips:
  • 特定の「設定値」だけを抜き出して、別の関数の引数に使いたい場合に重宝する。

/
function updateNetwork(value: AppConfig[‘apiEndpoint’ | ‘retries’]) {
console.log(“設定を更新中…”, value);
}

// OK!
updateNetwork(“https://api.example.com”);
updateNetwork(3);

// エラー! AppConfigに含まれない型や、指定したキー以外の型は弾かれる
// updateNetwork(true);

3. ブラウザの裏側で何が起きているか?

ここで少し、アーキテクトらしい視点をお裾分けしよう。

「TypeScriptでこんなに複雑な型を組んで、ブラウザの実行速度に影響はないのか?」と心配する後輩がたまにいる。答えは「ゼロ」だ。

TypeScriptの型システムは、コンパイル(トランスパイル)時にすべて消滅する。ブラウザが受け取るのは、型定義が一切剥ぎ取られた純粋なJavaScriptだ。

しかし、この「インデックスアクセス型」を駆使することで、ブラウザでエラーが出る前に、エディタ上でバグを100%殺せるという点に価値がある。実行時の `undefined` や `TypeError` は、開発者の怠慢だ。この型定義は、未来の自分やチームメイトへの「動く仕様書」なんだよ。

4. 実践:Tuple(タプル)型からの抽出

この手法の凄まじいところは、配列(タプル)にも応用できる点だ。

// 定数として定義された読み取り専用の配列
const ROLES = [‘admin’, ‘editor’, ‘viewer’] as const;

// 配列の要素の型をユニオン型として抽出する
// number をインデックスに指定するのがミソだ
type Role = (typeof ROLES)[number];
// 結果は: “admin” | “editor” | “viewer”

/

  • なぜこれが嬉しいのか?
  • 1. 配列(実体)に要素を追加するだけで、自動的に型(Role)も更新される。
  • 2. 型定義を二重に管理する必要がなくなる。

/
const currentRole: Role = ‘admin’; // OK

5. チーフアーキテクトからのアドバイス

いいかい、今回紹介した `Type[K1 | K2]` という手法は、単なるテクニックじゃない。「コードの整合性をシステム的に担保する」という哲学なんだ。

実務でコードを書く時は、常に自分に問いかけてほしい。
「今、俺は同じ名前や同じ型を二箇所に書いていないか?」と。

もし書いていたら、それはインデックスアクセス型の出番だ。`any` や `unknown` で逃げるのは最後の手。まずは型を正しく「抽出」できないか考えてみてくれ。

今日のまとめ

1. `Type[‘key1’ | ‘key2’]` は、複数のプロパティ型をユニオン型として抽出できる。
2. `keyof Type` と組み合わせれば、そのオブジェクトが持つ全値の型を動的に生成できる。
3. 配列(タプル)に対しては `[number]` を使うことで、中身の値をユニオン型に変換できる。

これができれば、君の書くコードは「ただ動くコード」から「変更に強い、プロのコード」へ進化する。

次は「Template Literal Types」との組み合わせについても話したいが……それはまた別の機会にしよう。まずは今日のこのテクニックを、今書いているそのコンポーネントにぶち込んでみてくれ。

ハッピーハッキング!

コメント

タイトルとURLをコピーしました