🐣

【TypeScript】配列から型を生成する方法

に公開

はじめに

配列から型を生成する方法についてまとめていきたいと思います。

基本構文

const arr = ["A", "B", "C"] as const;
type Example = (typeof arr)[number];
// → "A" | "B" | "C"
  • as const
    配列やオブジェクトの中身をリテラル型として固定します。同時に、プロパティがすべてreadonly(再代入不可) になります。

  • typeof arr[number]
    配列の要素を取り出してユニオン型に変換します。

具体例

例1 配列からユニオン型を作る

const languages = ["en", "ja", "fr", "de", "ko"] as const;

type Language = (typeof languages)[number];
// "en" | "ja" | "fr" | "de" | "ko"

function setLanguage(lang: Language) {
  console.log(`Language set to: ${lang}`);
}

setLanguage("ja"); 
setLanguage("cn"); // "cn" は型 'Language' に含まれないのでエラー

こんな感じ↓

ポイント

  • 配列に as const を付けないと、要素型が string に広がってしまうため注意。

例2 オブジェクトから型を生成する

const roles = {
  admin: "管理者",
  user: "一般ユーザー",
  guest: "ゲスト",
} as const;

type RoleKey = keyof typeof roles; // "admin" | "user" | "guest"
type RoleValue = (typeof roles)[keyof typeof roles];
// "管理者" | "一般ユーザー" | "ゲスト"

RoleKeyのところはこんな感じ↓

RoleValueのところはこんな感じ↓

ポイント

  • keyof typeof rolesでキーのユニオン型を取得。

  • (typeof roles)[keyof typeof roles] ですべての値の型をユニオン化。

  • as constをつけることで値が "管理者" のようなリテラル型に固定される。

例3 配列をもとにオブジェクト型を動的生成

const keys = ["id", "name", "email"] as const;

type User = {
  [K in typeof keys[number]]: string;
};
// { id: string; name: string; email: string }

こんな感じ↓

ポイント

  • typeof keys[number] で "id" | "name" | "email" を取得。

  • [K in typeof keys[number]]: stringでマッピング型を使い、動的にオブジェクト型を作成。

  • 配列を変更すると型も自動的に更新されるため、メンテナンス性が高い。

例4 オプショナルプロパティにしたい場合

例3のような動的生成では、すべてのプロパティが必須になります。
オプショナル(省略可能)にしたい場合は、次のように ? を付けます。

const keys = ["id", "name", "email"] as const;

type PartialUser = {
  [K in typeof keys[number]]?: string;
};
// { id?: string; name?: string; email?: string }

こんな感じ↓

ポイント

  • ?をつけることで、そのプロパティをオプショナルにできる。

  • 最終的に {} や { id: "1" } など、部分的なオブジェクトも許可される。

例5 Record型を使って同じことを短く書く

Record は TypeScript に用意されているユーティリティ型で、
「キーの集合」と「値の型」からオブジェクト型を生成できます。

const keys = ["id", "name", "email"] as const;

// 必須プロパティ
type User = Record<typeof keys[number], string>;
// { id: string; name: string; email: string }

// オプショナルプロパティ
type PartialUser = Partial<Record<typeof keys[number], string>>;
// { id?: string; name?: string; email?: string }

Userのところはこんな感じ↓

PartialUserのところはこんな感じ↓

ポイント

  • Record<K, T>は { [P in K]: T } の省略形。

  • Partial<T>はすべてのプロパティをオプショナルにするユーティリティ型。

  • Readonly<Record<...>>を使えばすべてを読み取り専用にできる。

まとめ

やりたいこと 記述例 結果
配列要素をユニオン型にする (typeof arr)[number] "A" | "B" | "C"
オブジェクトのキーを型にする keyof typeof obj "key1" | "key2"
オブジェクトの値を型にする (typeof obj)[keyof typeof obj] "val1" | "val2"
配列からオブジェクト型を作る(明示) { [K in typeof arr[number]]: T } { a: T; b: T }
配列からオプショナル型を作る(明示) { [K in typeof arr[number]]?: T } { a?: T; b?: T }
配列からオブジェクト型を作る(Record) Record<typeof arr[number], T> { a: T; b: T }
配列からオプショナル型を作る(Record) Partial<Record<typeof arr[number], T>> { a?: T; b?: T }
すべてを読み取り専用にする Readonly<Record<typeof arr[number], T>> { readonly a: T; readonly b: T }

Discussion