【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