type-challenges 中級編3: Last of Array, Pop, Promise.all
はじめに type-challengesとは
GitHubのtype-challengesリポジトリは、TypeScript型システムの理解を深めるための型クイズ問題集です。高品質な型を作成することで、バグを減らし、プロジェクトの保守性を向上させることができます。今回は中級編から3つの問題に挑戦します。
1. Last of Array
配列 T を受け取り、その最後の要素の型を返す汎用的な Last<T> を実装してください。
例
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1
私の回答
type Last<T extends any[]> = T extends [...infer Rest, infer U] ? U : never
解き方
-
T extends [...infer Rest, infer U]という条件型を使い、配列Tの最後の要素Uを推定します。 - 配列
Tの最後の要素がUとして推定される場合、その型Uを返します。 - そうでない場合は
neverを返します。
inferとスプレッド構文を使えれば問題無く解けますね
もう一つの奇抜な型も思いついたので紹介します。
type Last<T extends unknown[]> = T extends [...infer U, unknown] ? T[U['length']] : never
実際のjsで書くと
arr[arr.length - 1]
と書いたような書き方ですね
キモいので上の方がいいですね
2. Pop
配列 T を受け取り、最後の要素を除いた配列を返す汎用的な Pop<T> を実装してください。
例
type arr1 = ['a', 'b', 'c', 'd']
type arr2 = [3, 2, 1]
type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]
私の回答
type Pop<T extends any[]> = T extends [...infer U, unknown] ? U : []
解き方
-
T extends [...infer U, unknown]という条件型を使い、配列Tの最後の要素を除いた部分Uを推定します。 - 配列
Tの最後の要素を除いた部分がUとして推定される場合、その型Uを返します。 - そうでない場合は空配列
[]を返します。
Lastと一緒ですね。簡単。
3. Promise.all
Promise ライクなオブジェクトの配列を受け取る関数 PromiseAll に型を付けてください。戻り値は Promise<T> である必要があります。ここで、T は解決された結果の配列です。
例
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
// expected to be `Promise<[number, 42, string]>`
const p = PromiseAll([promise1, promise2, promise3] as const)
私の回答
declare function PromiseAll<T extends readonly unknown[]>(values: T): Promise<{
[K in keyof T]: Awaited<T[K]>;
}>;
解き方
- 結果の型は
Promise<...>で返す必要があります。 - 配列
Tの各要素T[K]に対して、Awaited<T[K]>を使用して、そのPromiseが解決された型を取得します。
組込みの型Awaited 型を使用することで、Promiseオブジェクトの解決された型を簡単に取得できます。
まとめ
このように、TypeScriptの型システムを理解し、実際に型を実装することで、より安全で保守性の高いコードを書くことができます。ぜひこれらの課題を解いて、型力UPしてみてください。