【入門編】配列の要素をユニオン型に変換する:keyofとtypeofの合わせ技 – TypeScript コア・型システムの基礎解析バイブル

こんにちは!TypeScriptの型システムの世界へようこそ。
普段フロントエンドやNode.jsでコードを書いていると、「あ、この定数配列の中身、全部型としても使い回したいな」と思う瞬間ってありませんか?

例えば、アプリケーションの状態や、許可された役割(ロール)、画面のタブの種類などを配列として定義したとき、その要素をわざわざ別のユニオン型として二重に書き直していませんか?

// 二重管理の悲劇(やりがちな書き方)
const ROLES = [‘admin’, ‘editor’, ‘viewer’] as const;
type Role = ‘admin’ | ‘editor’ | ‘viewer’; // ← 配列と二重に書いているので、配列側に新しい値を追加し忘れるバグの温床に!

これ、実はTypeScriptの `typeof` と `keyof` という強力な道具を組み合わせるだけで、配列から自動的にユニオン型を生成して、一発で同期させることができるんです。

ここをクリアすれば、あなたのTypeScriptの基本スキルは一気に一段上のレベルに到達しますよ。さあ、一緒にその仕組みの本質を紐解いていきましょう!

—

1. 魔法の公式:`typeof` と `keyof` の合わせ技

結論から言うと、配列からユニオン型を抽出する「型メタプログラミング」の定石は、たったこれだけです。

const ROLES = [‘admin’, ‘editor’, ‘viewer’] as const;

// 魔法のワンライナー
type Role = typeof ROLES[number];

なんだこの `[number]` って?って思いますよね。
大丈夫です、左から順番に解体して見ていけば、驚くほどロジカルな仕組みになっています。頭の中で型がどう評価されているのか、ステップバイステップで覗いてみましょう。

ステップ1:`as const` で「ただの配列」を「読み取り専用のタプル」にする

まず、配列の定義の末尾についている `as const`(constアサーション)。これが全ての起点です。

  • `as const` なし:ただの `string[]` 型(中身は何が入るか、何個あるか可変)
  • `as const` あり:`readonly [“admin”, “editor”, “viewer”]` 型(中身の要素と順番が完全に固定されたタプル型)

TypeScriptのコンパイラに対して、「この配列の内容は絶対に変わらないから、リテラル(値そのもの)のレベルで厳密に型を覚えておいて!」と伝える呪文です。

ステップ2:`typeof` で「値の空間」から「型の空間」へワープする

TypeScriptには、「値(JavaScriptとして実行される世界)」と「型(コンパイル時だけに存在する世界)」という2つの平行世界があります。

`typeof ROLES` は、値としてそこにある `ROLES` を、型の世界に召喚する演算子です。
これにより、型の世界で `readonly [“admin”, “editor”, “viewer”]` というタプル型が手に入ります。

ステップ3:`[number]` で配列の要素を全抜きする(ここがハイライト!)

ここが一番のキモです。JavaScriptで配列の何番目の要素かにアクセスするとき、`ROLES[0]` のように書きますよね。

TypeScriptの型においても同様に、タプル型に対してインデックスアクセスができます。
`number` は「任意の数値のインデックス」を意味します。つまり、`typeof ROLES[number]` は、「このタプルのすべてのインデックス(0, 1, 2…)に紐づく値の型を全部取ってきて、一つにまとめろ(ユニオンにしろ)!」 という意味になるのです。

  • 0番目の型:`”admin”`
  • 1番目の型:`”editor”`
  • 2番目の型:`”viewer”`

これらが結合され、最終的に `type Role = “admin” | “editor” | “viewer”;` という綺麗なユニオン型が爆誕します。

—

2. 実践!現場で使えるコード例

では、このテクニックが実際の開発現場でどう活きるのか、もう少し具体的なユースケースを見てみましょう。
例えば、Webアプリケーションの「タブメニュー」の状態を管理するコードを書いてみます。

// 1. タブの選択肢を配列として定義し、as const で固める
const TABS = [‘profile’, ‘settings’, ‘security’, ‘billing’] as const;

// 2. 配列から自動でユニオン型を生成する
type TabType = typeof TABS[number];
// 生成される型: “profile” | “settings” | “security” | “billing”

// 3. 実際の関数やコンポーネントで活用する
function switchTab(tab: TabType) {
console.log(`Switching to ${tab} tab…`);
}

// — 使い方と型の恩恵 —

// ✅ 正しい値:何のエラーも起きません
switchTab(‘profile’);

// ❌ 間違った値やタイポ:コンパイルエラーで即座に検知できる!
// switchTab(‘dashboard’);
// ➡️ エラー: 型 ‘”dashboard”‘ の引数を型 ‘TabType’ のパラメーターに割り当てることはできません。

もし将来、要件定義が変わって新しいタブ `’notifications’` を追加したくなったらどうでしょう?
`TABS` 配列に `’notifications’` を追加するだけで、`TabType` 型にも自動的に反映されます。型定義ファイルを別途書き換える必要は一切ありません。 これぞ、DRY(Don’t Repeat Yourself)原則の型システムにおける極みです。

—

3. 陥りやすい罠と文法エラー

このテクニックを使い始めの頃に、多くの開発者がハマる「よくあるミス」をいくつか紹介しておきますね。

罠1:`as const` を付け忘れる

const COLORS = [‘red’, ‘green’, ‘blue’]; // as const がない!
type Color = typeof COLORS[number];

さて、この `Color` 型はどうなるでしょうか?
答えは `”red” | “green” | “blue”` ではなく、ただの `string` になってしまいます。
`as const` がないと、TypeScriptは「これはあとから要素が追加されるかもしれない普通の `string[]` だな」と判断してしまうためです。配列から型を起こすときは、呪文の `as const` を忘れないように心がけましょう!

罠2:`keyof` と混同してしまう

今回のテーマは `typeof ARRAY[number]` ですが、オブジェクトのキーから型を作るときは `keyof` を使います。

  • 配列・タプルの場合:`typeof ARRAY[number]`
  • オブジェクトの場合:`keyof typeof OBJECT`

混同して `keyof typeof ARRAY` と書いてしまうと、配列のメソッド名(`map` や `filter`、`length` など)のユニオン型になってしまい、意図しない挙動に悩まされることになります。配列には `[number]`、オブジェクトには `keyof` と覚えておくと迷いませんよ。

—

まとめ

いかがでしたでしょうか?
今回解説した `typeof 配列[number]`(+ `as const`)のテクニックは、中規模から大規模なTypeScript開発において、コードの堅牢性と保守性を劇的に高める必須の武器です。

  • 値を定義した場所が、そのまま型の情報源(Single Source of Truth)になる。
  • 二重管理が消え、変更漏れやタイポによるバグをコンパイル時に完全にブロックできる。

ここをしっかりと自分のものにできれば、TypeScriptの型推論の面白さがグッと深まります。ぜひ明日のコーディングから取り入れて、スマートでミスのない型安全な開発を楽しんでくださいね!

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