xxxasdfghjk/type-challengesを埋める

Created Thu, 23 Mar 2023 23:45:52 +0900
1736 Words

勉強がてら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];
};

メモ

KTのキーである必要があるため、左辺に制約条件を追加する。

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[]が必要だった。

PropertyKeystring|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> を実装する。 Ctrue|falseリテラル型であり、trueのときTfalseのとき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

メモ

引数をスプレッド構文で引き受ける典型。