🛜

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