TypeScriptでオブジェクトのプロパティの一部をNonNullableにする
オブジェクトのプロパティをNullableからNonNullableにしたいな〜と思ってTypeScriptのUtility Typesでできないか見たんだけどできなさそうだった。
RequiredやPartialはプロパティそのものをrequiredにするかoptionalにするかなので、プロパティはrequiredでいいんだけど型をT | nullからTにしたいという感じ。
またNonNullable<T>は、Tからnullとundefinedを取り除いた型を定義してくれるが、プロパティではなくTそのものになるので今回は使えない。
どうしようかと思ったらmizchiさんがリプライをくれた。
conditional type でこれの逆やればよさそうhttps://t.co/3avJqKHyPI
— mizchi (@mizchi) March 24, 2022
TypeScript で既にある型から一部を nullable にする型を作る - Qiita
type Nullable<T, D extends keyof T> = {
[K in keyof T]: (K extends D ? T[K] | null : T[K])
}
type Obj = { a: string; b: number }
type NullableObj = Nullable<Obj, 'a' | 'b'> // => { a: string | null; b: number | null }
これを自分で考え出せるようになりたいが……。
まずジェネリクスの1つめの引数Tはnullableにしたいプロパティを持つオブジェクト、2つめのD extends keyof Tは、Tのプロパティ名のstring型(↑の例だと'a' | 'b')の継承型(extends)になっている。
実際の型は、まずプロパティがK in keyof Tなので、Tのプロパティ('a' | 'b')が全て入る。
値は、Conditional TypesでK extends Dに当てはまるかによって条件分岐する。
Kはプロパティで、Dはジェネリクスの2つめの引数で受け取った値。DはD extends keyof Tなので、Tのプロパティのうちどれか(もしくは全て)になる。
全てのプロパティK (K in keyof T)のうち、ジェネリクスで渡されたプロパティD (D extends keyof T)だけが条件分岐のK extends Dでtrueになるので、それらだけがT[K] | nullになる。
T[K]はそのままプロパティが持つ値の型になるので、stringならstring | nullになるしnumberならnumber | nullになる。
結果として、ジェネリクスの2つめの引数にnullableにしたいプロパティを渡せばそのプロパティはnullableになり、渡さなければならないということになる。
すげえこれ、どうやって思いつくんだ……。
自分はnon nullableにしたかったので、上記の逆をやればよい。
type NonNullable<T, D extends keyof T> = {
[K in keyof T]: (K extends D ? NonNullable<T[K]> : T[K])
}
type Obj = { a: stirng | null; b: number | null }
type NonNullableObj = NonNullable<Obj, 'a' | 'b'> // => { a: string; b: number }
ここでNonNullable<T>が生きてくる。