🛜
TypeScriptのConditional Typesを知ろう
概要
別記事を書くにあたりConditional Typesを理解しようの会
Conditional Typesとは
条件付き型制約という。型定義をするときに条件分岐して定義させるというもの。
参考: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html
使い方
以下のようにHoge型を定義する際に使用。JavaScriptの三項演算子のイメージ。
interface Value {
value: string;
}
interface KeyValue {
key: string;
value: string
}
// 型定義をする際の条件(Conditional Types)の条件文で使用、↓の場合はstringになる
type Hoge = KeyValue extends Value ? string : number; // string
// 使用例
const hoge: Hoge = "1"; // ✅OK
const hoge: Hoge = 1; // 🚫エラーになる
ジェネリクス型などを使用した書き方はこんな感じ。
// 型の引数Tが任意の配列型であればそのまま返す、違う場合はnumber型にする
type Hoge<T> = T extends any[] ? T : number;
const hoge: Hoge<string[]> = ["1", "2", "3"]; // 引数がstring[]なので型はstring[]
const fuga: Hoge<string> = 1; // 引数がstringなので型はnumber
ちなみに、書き方は A extends B ? C : D
で統一されている。
extendsは型にのみ使用できるため、AやBも型に関する比較のみ有効である。
const hoge: number = 0
type Hoge = typeof hoge extends number ? string : number; // ✅OK
type Hoge2 = typeof hoge === number ? string : number; // 🚫構文がエラー
type Hoge2 = hoge extends 0 ? string : number; // 🚫hogeが型でないのでエラー
const fuga: Hoge = '1' // typeof hogeはnumberなのでstring型になる
ユニオン型を用いた比較
type Hoge<T, K> = T extends K ? 'string' : 'number'
type Fuga = Hoge<'a' | 'b' | 'c', 'a'>
というようにextendsの左辺がユニオン型になっている場合、
Tの1つ目'a'とKの'a'を比較、extendsの条件がtrueなのでstring型
↓
Tの1つ目'b'とKの'a'を比較、extendsの条件がfalseなのでnumber型
↓
Tの1つ目'c'とKの'a'を比較、extendsの条件がfalseなのでnumber型
↓
それぞれの出力結果をユニオン型として string | number
という考え方になりFugaは string | number
となる。
type Hoge<T, K> = T extends K ? 'string' : never
type Fuga = Hoge<'a' | 'b' | 'c', 'a'>
の場合は同じ考え方でFugaが string
となる。
※ never型についてはこちら
Discussion