メインコンテンツまでスキップ

配列から全要素の型を生成する

前ページでは、配列から全要素の型を生成する方法が登場しました。

ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
 
type Currency = (typeof currencies)[number];
type Currency = "CNY" | "EUR" | "GBP" | "JPY" | "KRW" | "USD"
ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
 
type Currency = (typeof currencies)[number];
type Currency = "CNY" | "EUR" | "GBP" | "JPY" | "KRW" | "USD"

typeof currencies[number]という書き方は、初めて見ると理解に苦しむコードかもしれません。そのためより詳しく説明します。

前ページのコードを観察する

配列からある要素の型を生成するコードについて、前ページに続き通貨の配列でもう一度確認します。

ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
type Currency = (typeof currencies)[2];
type Currency = "GBP"
ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
type Currency = (typeof currencies)[2];
type Currency = "GBP"

ここで、typeof currencies[2]2は、前ページでリテラル型と説明していますが本当でしょうか?次のコードで確認してみます。

ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
const index = 2 as const;
type Currency = (typeof currencies)[index];
'index' refers to a value, but is being used as a type here. Did you mean 'typeof index'?2749'index' refers to a value, but is being used as a type here. Did you mean 'typeof index'?
ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
const index = 2 as const;
type Currency = (typeof currencies)[index];
'index' refers to a value, but is being used as a type here. Did you mean 'typeof index'?2749'index' refers to a value, but is being used as a type here. Did you mean 'typeof index'?

2が値として解釈されるコードではエラーになってしまいました。

では明確にリテラル型だとわかるコードも試してみましょう。

ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
type Index = 2;
type Currency = (typeof currencies)[Index];
type Currency = "GBP"
ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
type Index = 2;
type Currency = (typeof currencies)[Index];
type Currency = "GBP"

これでtypeof currencies[2]2はリテラル型であることがはっきりしました。

数値のリテラル型とnumber

2のリテラル型とnumber型の関係を集合で表現すると、2numberと書くことができます。他の表現をすると、012..など数値のリテラル型のいずれかの型として振る舞うのがnumber型です。

「いずれかの型」といえばユニオン型の出番です。

📄️ ユニオン型

TypeScriptのユニオン型(union type)は「いずれかの型」を表現するものです。

number型の代わりにリテラルのユニオン型を使ってみましょう。

ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
type Currency = (typeof currencies)[0 | 1 | 2 | 3 | 4 | 5];
type Currency = "CNY" | "EUR" | "GBP" | "JPY" | "KRW" | "USD"
ts
const currencies = ["CNY", "EUR", "GBP", "JPY", "KRW", "USD"] as const;
type Currency = (typeof currencies)[0 | 1 | 2 | 3 | 4 | 5];
type Currency = "CNY" | "EUR" | "GBP" | "JPY" | "KRW" | "USD"

0 | 1 | 2 | 3 | 4 | 5型でもcurrencies配列から全要素の型を生成することができました。このようにnumber型は数値のリテラル型のワイルドカードとして振る舞うことがわかります。

一般化する

このページの締めくくりに一般化したコードを示します。

ts
type List = (string | number | boolean)[];
type Elem = List[number];
type Elem = string | number | boolean
ts
type List = (string | number | boolean)[];
type Elem = List[number];
type Elem = string | number | boolean

List型からList[number]という書き方ですべての要素の型であるstring | number | booleanが生成できました。

アンチパターンの紹介

次のように具体的なインデックスで同じ型を生成することは可能ですが、アンチパターンなので注意してください。

ts
type List = (string | number | boolean)[];
type Elem = List[0]; // 避けるべき書き方
type Elem = string | number | boolean
ts
type List = (string | number | boolean)[];
type Elem = List[0]; // 避けるべき書き方
type Elem = string | number | boolean

この書き方がアンチパターンである理由はList型をタプル型だと混乱させてしまう可能性があるためです。List[0]は特定の要素から型を生成しているため、各要素の型が同じ型ではない、つまりListが配列型ではなくタプル型だからこの書き方をしていると誤解させる可能性があります。配列型はどの要素の型も同じものとして扱うので、List[number]の書き方が適切です。