勉強がてらtype-challengesなる TypeScript の問題集をやってみる。
warm-up
13・Hello World
解答方法の verify のための設問。
解答
type HelloWorld = string;
メモ
なし
easy
4・Pick
Pick<T,K>
をそれ自身を使わずに実装する問題。
解答
type MyPick<T, K extends keyof T> = {
[key in keyof T as key extends K ? key : never]: T[key];
};
メモ
K
はT
のキーである必要があるため、左辺に制約条件を追加する。
7・ReadOnly
ReadOnly<T>
をそれ自身を使わずに実装する問題。
解答
type MyReadonly<T> = {
readonly [key in keyof T]: T[key];
};
メモ
特になし。
11・Tuple to Object
与えられた各タプル要素を key/value とする型TupleToObject<T>
を実装する問題。
解答
type TupleToObject<T extends readonly PropertyKey[]> = {
[t in T[number]]: t;
};
メモ
[t in T[number]]:t
までは思いついたが、一つエラーが消えず。
諦めて答えを見ると、制約条件にPropertyKey[]
が必要だった。
PropertyKey
はstring|number|symbol
の alias で、[]
の角カッコ内で書ける型全体を指すらしい。自身を key/value としたオブジェクトにするためには、与えられた要素がプロパティで指定可能な型という条件は確かに必要。
14・First of Array
タプル型を引数にとり、その先頭要素の型を返すFirst<T>
を実装する問題。
解答
type First<T extends any[]> = T extends [] ? never : T[0];
メモ
indexed access type を使う。
タプルの大きさが 1 以上の場合はT[0]
で問題ないが、[]
が渡されたときのみnever
を返す必要がある。
14・Length of Tuple
タプル型の大きさをリテラル型で返すLength<T>
を実装する問題。
解答
type Length<T extends readonly any[]> = T["length"];
メモ
T["length"]
でタプルの大きさのリテラル型を返すらしい。知らなかった。(ここで知った)[https://www.forcia.com/blog/002377.html]
43・Exclude
Exclude<T>
を実装する問題。
解答
type MyExclude<T, U> = T extends U ? never : T;
メモ
ユニオン型T
を展開した上で評価するのがポイント。
例えばMyExclude<'a' | 'b' | 'c', 'a' | 'b'>
が評価されるときは
'a' extends 'a' | 'b' ? never : 'a'
| 'b' extends 'a' | 'b' ? never : 'b'
| 'c' extends 'a' | 'b' ? never : 'c'
と展開され、結果'c'
になる
189・Awaited
Promise<T>
を unwrap してT
を取り出すAwaited<U>
を実装する問題。
解答
type MyAwaited<T> = T extends { then: (onfulfiled: (arg: infer U) => any) => any } ? MyAwaited<U> : T;
メモ
想定解がよくわからない。
再帰的にMyAwaited<U>
を適用し、Promise<T>
にマッチしなくなったときの型を返す。
※追記
JavaScriptだとthenメソッドを持つオブジェクトはPromiseと判定されるらしい。TypeScriptでは、PromiseLike<T>
なる型でそれを表現できる。それを使って答えると次のようになる。
type MyAwaited<T> = T extends PromiseLike<infer U> ? MyAwaited<U> : T
268・If
If<C,T,F>
を実装する。
C
はtrue|false
リテラル型であり、true
のときT
、false
のときF
型を返す。
解答
type If<C extends true|false, T, F> = C extends true ? T : F
メモ
特になし
533・Concat
2つのタプルをとり、連結した1つのタプルを返す Concat<T1,T2>
を実装する。
解答
type Concat<T extends readonly any[], U extends readonly any[]> = [...T,...U]
メモ
型の文脈でもspread構文は存在するらしい。
898・includes
タプルに特定の型が存在するか判定するIncludes<T,U>
を実装する。
解答
type Includes<T extends readonly any[], U> = T extends [infer V , ...infer Rest] ? (Equal<V,U> extends true ? true : Includes<Rest,U>) : false
メモ
Equal<T,U>
を使うのは盲点。それがわかれば再帰で実装するのみ。
3057・Push
タプルに型を1つ追加するPush<T,U>
を実装する。
解答
type Push<T extends readonly any[], U> = [...T, ...[U]]
メモ
特になし。
3312・Parameters
関数を受け取り引数のタプルを返すParameters<F>
を実装する。
解答
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never
メモ
引数をスプレッド構文で引き受ける典型。