Type Challengesを解いていく(初級)
以前に少しやった記憶があるが、どこまでやったか忘れたのでメモしながら再度進めていく。
これまたReadonly<T>
ではダメなやつ。
自分の解答
type MyReadonly<T> = { readonly [K in keyof T]: T[K] }
readonly
を付与する場所を間違え続けていた・・・。
配列をユニオンにするやつ。
自分の解答
type TupleToObject<T extends readonly any[]> = { [K in T[number]]: K }
他の問題もそうだけど、知らないと解けない感が増している・・・。
あとany[]
の箇所はstring[]
などにしたほうが良さそうではある。
配列の最初の要素の型を取得。
自分の解答
type First<T extends any[]> = T[0] extends undefined ? never : T[0]
としたけど、これだと最後のテストケースが通らない。
配列に要素がないことを確認するためにはどうすればよいか?ということで、[][number]
の型を確認し、これがnever
になることがわかったため、以下のような解答にするとテストケースがすべて通った。
type First<T extends any[]> = T[number] extends never ? never : T[0]
ただ、他の解答を確認してみたところ、T extends []
やT["length"] extends 0
としており、この方が直感的だなと思った。
自分の解答
type Length<T> = T['length']
さきほどの First of Array で解答を知ってしまった・・・😂
ただ、これだけだと// @ts-expect-error
のテストケースが通らないので、以下のように修正した。
type Length<T extends Readonly<any[]>> = T['length']
投稿されていた他の解答もほぼ以下のような感じだった。
type Length<T extends readonly any[]> = T['length']
自分の解答
これ、全然わからなかった・・・。何となくextends
を使うのではないか、ということは分かったが、それ以上どのように書けばよいか検討もつかなかった。
答えは
type MyExclude<U, T> = U extends T ? never : U;
とのことで、自分は Conditional Types における Union types の分配則を理解していないことがわかった。
参考:TypeScript 2.8 の Conditional Types について - Qiita
上記記事の例がわかりやすいが、(T1 | T2) extends U ? X : Y
は(T1 extends U ? X : Y) | (T2 extends U ? X : Y)
と評価される。never
だと分かりにくいが、試しにnever
を1
に変えて
type MyExclude<T, U> = T extends U ? 1 : T
としてみると、以下のように分配則が適用されていることがわかる。
type Test = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c' | 1
自分の解答
ここで使うのがinfer
か!というわけで以下を試す。
type MyAwaited<T> = T extends Promise<infer U> ? U : never;
一部動いたが、Promise
がネストしているケースとエラーのケースが通らない。まずはエラーのケースが通るようにする。
type MyAwaited<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
その後、最後のテストケースが通るように書き換えたが、これだとPromise<Promise<Promise<string>>>
とかは通らないよねぇ・・・。
type MyAwaited<T extends Promise<any>> = T extends Promise<infer U>
? U extends Promise<infer K> ? K : U
: never;
というわけで、少し書き換えて以下のようにするが、MyAwaited<U>
の箇所でエラーになってしまう(U
がPromise<any>
を満たさない)。
type MyAwaited<T extends Promise<any>> = T extends Promise<infer U> ? MyAwaited<U> : T;
というわけで、ここで解答例を見るとU
のチェックも行っていた。そりゃそうか、という感じだが、惜しかったな〜。
type MyAwaited<T extends Promise<unknown>> =
T extends Promise<infer U>
? (U extends Promise<unknown> ? MyAwaited<U> : U)
: never;
189 - Awaited · Issue #5608 · type-challenges/type-challenges
条件分岐っぽいやつ。
自分の解答
なんかすぐ解けた。
type If<C extends boolean, T, F> = C extends true ? T : F;
他の人の解答も確認したがだいたいこんな感じだった。268 - If · Issue #1313 · type-challenges/type-challenges の例とかは割と丁寧かもしれない。
type If<C extends boolean, T, F> = C extends true ? T : C extends false ? F : never
自分の解答
これもすぐ解けた。
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]
他の解答もこんな感じだった。
自分の解答
初見では
type Includes<T extends readonly any[], U> = U extends T[number] ? true : false;
かなと思ったが、通らないケースが複数ある。
色々試行錯誤したけど結局わからなかったので解答を確認することに。ここで、初めて日本語の解答例(解答者)がないことに気がつく。他の解答を見る限りでも、テストケースが通らないものも多かったので結構難しい問題なのかもしれない。
以下が解答例。スプレッド構文で配列の最初の要素を取得しU
と比較、それをループしています。
type Includes<T extends readonly any[], U> = T extends [infer F, ...infer Rest]
? Equal<F, U> extends true
? true
: Includes<Rest, U>
: false;
898 - Includes · Issue #10678 · type-challenges/type-challenges
以下、他に参考にした記事。みんな「これ初級か?」と言っている。
自分の解答
Push とほぼ同じだった。
type Unshift<T extends any[], U> = [U, ...T]
記念すべき初級最後の問題(2022年5月現在)。
自分の解答
恥ずかしながら何もわからなかった😇
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
3312 - Parameters · Issue #10646 · type-challenges/type-challenges
なるほどinfer
ね・・・。infer
に慣れていないことが露呈してきたので鍛えていきたい💪
(現時点でもかなり Type Challenges を解く前よりも鍛えられている気がするが。)
中級に挑戦してく😎
自分の解答
type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer U ? U : never;
流れるようにこの解答を導き出せるようになった自分を褒めたい。
中級もやっていこうと思ったが、スクラップが長くなりすぎるので分割することにした。