Type Challenges 中級編14: ObjectEntries, Shift, Tuple to Nested Object
はじめに
GitHubのtype-challengesリポジトリは、TypeScript型システムの理解を深めるための型クイズ問題集です。高品質な型を作成することで、バグを減らし、プロジェクトの保守性を向上させることができます。今回も中級編から3つの問題に挑戦します。
1. ObjectEntries
この課題では、TypeScriptのオブジェクト型をキーと値のペアのタプルに変換する型を実装します。これはJavaScriptの Object.entries
の型版です。
例
interface Model {
name: string;
age: number;
locations: string[] | null;
}
type ModelEntries = ObjectEntries<Model>; // ['name', string] | ['age', number] | ['locations', string[] | null];
私の回答
type ObjectEntries<T> = {
[K in keyof T]-?: [K, T[K]]
}[keyof T];
解き方
-
ObjectEntries
型は、オブジェクトの各プロパティキーに対して、そのキーと対応する値のペアを生成するマッピングを作成します。 - 最終的に、各ペアのユニオン型を返します。
このようにして、オブジェクトのすべてのプロパティをキーと値のペアのタプルとして取得することができます。
早速前回学んだ-?
が役立ちましたね!
2. Shift
この課題では、配列の先頭要素を削除した新しい配列型を返す型を実装します。これはJavaScriptの Array.shift
の型版です。
例
type Result = Shift<[3, 2, 1]>; // [2, 1]
私の回答
type Shift<T extends Array<unknown>> = T extends [infer _, ...infer Rest] ? Rest : [];
解き方
-
Shift
型は、ジェネリック型T
が配列であり、その配列が非空の場合に先頭要素を除いた残りの配列を返すようにします。 -
infer
キーワードを使用して先頭要素と残りの要素を抽出します。
このようにして、配列の先頭要素を削除した新しい配列型を作成することができます。
3. Tuple to Nested Object
この課題では、文字列のタプル型 T
と任意の型 U
を受け取り、タプルをネストされたオブジェクト型に変換する型を実装します。
:::note alert
いや急に簡単?inferさえできればできる。
:::
例
type A = TupleToNestedObject<['a'], string>; // {a: string}
type B = TupleToNestedObject<['a', 'b'], number>; // {a: {b: number}}
type C = TupleToNestedObject<[], boolean>; // boolean. if the tuple is empty, just return the U type
私の回答
type TupleToNestedObject<T, U> =
T extends [infer F, ...infer Rest] ? { [K in F & string]: TupleToNestedObject<Rest, U> } : U;
解き方
-
TupleToNestedObject
型は、タプルT
が非空である限り、最初の要素F
をキーとし、そのキーの値が残りのタプルRest
をネストしたオブジェクト型となるように再帰的に処理します。 - タプルが空の場合は、型
U
を返します。
このようにして、タプルを元にネストされたオブジェクト型を生成することができます。
{ [K in F & string]: TupleToNestedObject<Rest, U> }
(Fは文字列リテラル)
でオブジェクトを作れることも覚えておこう。
まとめ
これらの課題を通じて、TypeScriptの型システムに対する理解が深まり、複雑な型操作のスキルが向上します。特に、タプルや配列の操作、オブジェクト型のプロパティの操作に関する知識を強化できます。ぜひ、これらの課題に挑戦して、型の理解を深めてください。