Open2
TypeScriptメモ

ある型がneverかどうかで条件分岐をさせたいときは、[T] extends [never]
を使う
type aa<T> = [T] extends [never] ? true: false
type aaa<T> = T extends never ? true: false
type c = aa<never> // true
type d = aaa<never> //never

OCPの思想に沿った設計をしている時に出てきがちな、
- ある型がtupleの場合、
[number, number]
と型関数F<T>
から[F<number>, F<number>]
を作りたい - ある型が配列の場合、
number[]
と型関数F<T>
からF<number>[]
を作りたい - それ以外の場合、
number
と型関数F<T>
からF<number>
を作りたい
を実現する型の書き方は次のようになる
type IsTuple<T> = T extends readonly any[]
? number extends T['length']
? false
: true
: false
type ApplySomeFunc<I> =
IsTuple<I> extends true
? {[K in keyof I]: F<I[K]>}
: I extends Array<infer U>
? Array<F<U>>
: F<I>
TypeScriptでは型関数の引数に型関数を渡すことはできない(渡したい…)ので、ApplySomeFunc
に相当する型関数はF<T>
の数だけ書くことになる。isTuple
はutils/type.ts
的なところに入れておけばいい。
I
がtupleの時(たとえば、[number, number]
とする)に{[K in keyof I]: SomeFunc<I[K]>}
と書くことで、[F<number>, F<number>]
が得られる挙動は、Mapped Tuple Typeと呼ばれる、Mapped Type とは少し異なるイレギュラーな挙動である。
type SomeTuple = [number, number]
type A = Tuple['map'] // これは型エラーにはならない
このように作成したSomeTuple型は、Array.prototypeが持っているメソッドを型情報として持つ。そのため、Mapped Typeのルールに従うと{[K in keyof I]: SomeFunc<I[K]>}
はat
などのArray
由来のプロパティを持つはずだが、実際にはそうはならない。ルールの一貫性より使いやすさが優先された結果、この挙動が追加された。(そのときのリリースノート)
参考にさせていただいた記事: