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>
が生きてくる。