Typescript UtilityTypes まとめる

React に続き TypeScript も今週から始めてみたけど、おもろい!型がおもろい!カタが! UtilityTypes について、少しずつ分析してみる。

Exclued

U に割り当て可能な T のキーを全て exclued (除外)した T を構築
type Exclude<T, U> = T extends U ? never : T

Tをstring 、Uを number とした場合
extends ? の結果 string は number の部分型ではないため false。
よって string から 除外されるものはなく、そのままプリミティブ型の string となる。

type E = Exclude<string, number>
// E の型は プリミティブ型 string

const obj: E = "sample"


TをSample 、UをExample とした場合
keyof によるそれらの union 同士のextends ? において、U に対して T の name が部分型であるため true。
よって name が never となり、それ以外の age が T となる。

type E = Exclude<keyof Sample, keyof Example> 

// E の型はリテラル型 "age"

const obj: E = "age"

type Sample = {
    name: string
    age: number
}
type Example = {
    name: string
    weight: number
}

Omit

TからKをOmit (省く)したオブジェクトにマッピング
Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
Pick と逆の動作 (Pick はTからKを選んでマッピング)
シグネチャの通り、string | number | symbol の部分型のみ省くことが可能

Exclued が使用されている

// 上の方に書いた Exclued のシグネチャ
Exclude<T, U> = T extends U ? never : T

// Omit ではその変化球
// Exclude の T を keyof の union 、U を K で表しており、全体としてMappedTypes である
 type Omit<T, K extends string | number | symbol>
 = { [P in Exclude<keyof T, K>]: T[P]; }

Omit の T を Sample、 その中から省きたい K を "name" | "age" とした場合
Exclued の K である "name" | "age" に割り当て可能な Sample である T の keyof による union は、Sample の構造より、name, age となる。
そのため、それらが除外され weight が残る。

Omit<Sample, "name" | "age">

type Sample {
    name: string,
    age: number,
    weight: number
}

残った weight は P に掛かっており、その型は T[P]であらわすことから 、つまりMappedTypes であり、in によって P が weight 、T[P] の評価により、その型は number のオブジェクトにマッピングされる。

// 型引数を決める前
type Omit<T, K extends string | number | symbol>
 = { [P in Exclude<keyof T, K>]: T[P]; }

// 型引数を決めた後
Omit<Sample, "name" | "age">
= {weight: number}

// O の型は {weight: number} 型
type O = Omit<Sample, "name" | "age">

// Sample から name, age が省かれるため weight のみ指定できる
const obj: O = {weight: 4.8}