【TypeScript】Object.entries()の返り値を厳格に型定義する
const obj = { a: 1, b: 'foo', c: true,};Object.entries(obj); // => [string, string | number | boolean][]
Object.entries()
の返り値でkeyが一律string
になってしまうため、返り値の型を厳格にしたい。
上記の例だと(["a", number] | ["b", string] | ["c", boolean])[]
と型推論されるのが理想。
1. ユーティリティ型の実装
Mapped Typesを使う方法
type Obj = { a: number; b: string; c: boolean;};
type Entries<T extends Record<string, unknown>> = { [K in keyof T]: [K, T[K]];}[keyof T][];
type Result = Entries<Obj>; // => (["a", number] | ["b", string] | ["c", boolean])[]
Mapped Typesでkeyとvalueを関連付ける方法1。今回の中だと一番シンプルな気がする。
Distributive Conditional Typesを使う方法
type Entries< T extends Record<string, unknown>, K extends keyof T = keyof T,> = (K extends any ? [K, T[K]] : never)[];
type Result = Entries<Obj>; // => (["a", number] | ["b", string] | ["c", boolean])[]
Distributive Conditional TypesでUnion型のkeyを分散させ、keyとvalueを関連付ける方法。
inferを使う方法
type Entries<T extends Record<string, unknown>> = (keyof T extends infer U ? U extends keyof T ? [U, T[U]] : never : never)[];
type Result = Entries<Obj>; // => (["a", number] | ["b", string] | ["c", boolean])[]
タプルにする方法
type Entries<T extends Record<string, unknown>> = [keyof T, T[keyof T]][];
type Result = Entries<Obj>; // => [keyof Obj, string | number | boolean][]
他と比べればシンプルだが、keyとvalueの両方がUnion型なタプルとなってしまうので厳格さは欠けてしまう。
2. ラッパー関数の実装
function objectEntries<T extends Record<string, unknown>>(obj: T): Entries<T> { return Object.entries(obj) as Entries<T>;}
objectEntries(obj); // => (["a", number] | ["b", string] | ["c", boolean])[]
参考
-
keyが
number
の場合string
に変換されてしまうため、keyがstring
なオブジェクトのみを受け付けるようにしている ↩ -
Typescript Key-Value relation preserving Object.entries type ↩