🦔

2021/06/14に公開

# 基本の操作

## タプルから値をを取り出す。

``````type First<T extends any[]> = T extends [infer R, ... infer Rest] ? R : []

type __First_Ex1 = First<[1,3]>  // 1
type __First_Ex2 = First<[]>  // never
``````
``````type Last<T extends any[]> = T extends [... infer _, infer Last] ? Last : []

type __Last_Ex1 = Last<[1,2,3]> // 3
type __Last_Ex2 = Last<[1]> // 1
type __Last_Ex3 = Last<[]> // never
``````

## 文字列版

``````type First<T extends string> = T extends `\${infer First}\${infer Last}` ? First : never

type __First_Ex1 = First<"abc">  // a
type __First_Ex2 = First<"d">  // d
type __First_Ex3 = First<"">  // never
``````

## 再帰を使う

タプルの長さは[1,2,3]["length"]として出せるが、

``````type LengthOfTuple<
T extends any[] = [],
Counter extends unknown[] = []
> =  T extends [infer _, ... infer Rest] ? LengthOfTuple<Rest, [...Counter, unknown]>  : Counter["length"]

type __LengthOfTuple_Ex1 = LengthOfTuple<[1,2,3]> // 3
``````

# 可読性をあげるために

## 型変数を使って正しい名前をつける

``````type NumberToTuple<T extends number, U extends any[] = []> R["length"] extends N ? R : NumberToTuple<N, [0, ...U]>
``````
``````
type Count = "count"
type NumberToTuple<T extends number, Counter extends Count[] = []> R["length"] extends N ? R : NumberToTuple<N, [Count, ...U]>

``````

## And (&&)

conditional type でandを使う場合はタプルを使って判定するといい。

``````// これは冗長
type BothString<A extends any, B extends any> = A extends string ? B extends  string ? true : false : false

type False = BothString<"",0>
type True = BothString<"foo", "bar">
``````
``````type BothString<A extends any, B extends any> = [A, B] extends [string, string]? true : false

type False = BothString<"",0>
type True = BothString<"foo", "bar">
``````

## Or (||)

``````type BothString<A extends any, B extends any> = [A, B] extends [string, any] | [any, string] ? true : false
// こっちの方が好き。
type BothString_<A extends any, B extends any> = [A, B] extends {0: string} | {1: string } ? true : false

type False = BothString<0,0>
type True_ = BothString<0,"">
type True1 = BothString<"",0>
type True2 = BothString<"foo", "bar">
``````

# 配列が空になるまで繰り返す。

``````type IsEmpty1<T extends any[]> = T["length"] extends 0 ? true : false

type IsEmpty2<T extends any[]> = T extends [] ? true : false

type IsEmpty3<T extends any[]> = T extends [infer First, ...infer Rest] ? false : true
``````

# 型のキャスト

``````type NumberToTuple<N extends number, R extends any[] = []> = R["length"] extends N ? R : NumberToTuple<N, [unknown, ...R]>
type Add<A extends number, B extends number> = [...NumberToTuple<A>, ...NumberToTuple<B>]["length"]
// type Five = Add<2, 3> // 5
``````

これをnumber型を型変数に取るNumberToString型で使いたい。

しかしAddの結果をそのまま使おうとすると、`Add<A, B>` 型はnumberとして推論されないため、エラーになる。

``````type NumberToString<T extends number> = `\${T}`

type NG<A extends number, B extends number> = NumberToString<Add<A, B>> // Type 'Add<A, B>' does not satisfy the constraint 'number'.
``````

``````type OK_1<A extends number, B extends number> =  Add<A, B> extends number ? NumberToString<Add<A, B>> : never

// 型変数を使った方がスッキリ書ける
type OK_2<A extends number, B extends number, Added = Add<A, B>> = Added extends number ? NumberToString<Added> : never
``````

これはExtractを使うともっと簡潔に書ける。

``````
type OK_3<A extends number, B extends number> = NumberToString<Extract<Add<A, B>, never>>
type _OK_3_Limit = OK_3<43,43>
``````

``````type NumberToTuple<N extends number, R extends any[] = []> = R["length"] extends N ? R : NumberToTuple<N, [unknown, ...R]>
type Add<A extends number, B extends number> = Extract<[...NumberToTuple<A>, ...NumberToTuple<B>]["length"], number>
// type Five = Add<2, 3> // 5

type NumberToString<T extends number> =`\${T}`

type OK<A extends number, B extends number> =  ShouldNumber<Add<A, B>>

// type _OK__limit = OK_2<43,43> // Type instantiation is excessively deep and possibly infinite.(2589)
``````

ただ可読性が良くなる一方でスタックの上限があがってしまう。

``

# 再帰型の限界を超える。

``````type NumberToTuple<N extends number, R extends any[] = []> = R["length"] extends N ? R : NumberToTuple<N, [unknown, ...R]>
type Add<A extends number, B extends number> = [...NumberToTuple<A>, ...NumberToTuple<B>]["length"]

type __Add_Limit = Add<43,43>  // 86が限界。
``````

ただ、87以上の和の計算ができないかというとそうでもない。

``````type Add_Three<A extends number, B extends number, C extends number> = [...NumberToTuple<A>, ...NumberToTuple<B>, ...NumberToTuple<C>]["length"]

type __Add_Limit = Add<43,43, 1>  // 各変数には43までしか入れられないが、横に増やすことはできる。
``````

なので、うまい具合に分割する再帰と組み合わせることでもう少し先に進める。

といっても今の実装でも限界は超えてる。

``````type NumberToTuple<N extends number, R extends any[] = []> = R["length"] extends N ? R : NumberToTuple<N, [unknown, ...R]>

// type _NG = NumberToTuple<46> // Type instantiation is excessively deep and possibly infinite.(2589)

// 46を作れた。
type _Over46 = NumberToTuple<46, NumberToTuple<1>>["length"]
``````

なので、これを再帰的につかう型を用意すれば良い。

そのためには上限が来る前にNumberToTupleを返すようにしてあげないとならない。

なのでこう改良してあげる。

``````type NumberToTuple_With_Limit<
N extends number,
Limit extends number,
PreviousResult extends unknown[] = [],
Counter extends unknown[] = []
> = PreviousResult["length"] extends N ? PreviousResult :
Counter["length"] extends Limit ? PreviousResult : // 最大試行回数
NumberToTuple_With_Limit<N, Limit, [...PreviousResult, unknown], [unknown, ...Counter]>

type __Ex1 = NumberToTuple_With_Limit<5, 10>["length"] // 5
type __Ex2 = NumberToTuple_With_Limit<100, 40>["length"] // 40
``````

これを使うとLimitを超える場合は一旦再帰を抜ける用になる。

これを使うことで

``````type NumberToTuple_With_Limit<
N extends number,
Limit extends number,
PreviousResult extends unknown[] = [],
Counter extends unknown[] = []
> = PreviousResult["length"] extends N ? PreviousResult :
Counter["length"] extends Limit ? PreviousResult : // 最大試行回数
NumberToTuple_With_Limit<N, Limit, [...PreviousResult, unknown], [unknown, ...Counter]>

type Limit = 10

type NumberToTuple<
N extends number,
PreviousResult extends unknown[] = [],
NextResult extends unknown[] = NumberToTuple_With_Limit<N,Limit, PreviousResult>
> = PreviousResult["length"] extends N ? PreviousResult :
NumberToTuple<N, NextResult>

type __100 = NumberToTuple<100>["length"] / 100
``````

26~29後半くらいが最適な値だった気がする。

ログインするとコメントできます