Typescriptで深層キーオブ:型安全なコードを実現する
Typescript: ネストされたオブジェクトのディープキーオブ
Typescriptでネストされたオブジェクトのディープキーオブを取得する方法はいくつかあります。この解説では、代表的な3つの方法とそのメリットとデメリットを紹介します。
方法
keyof
とtypeof
を組み合わせる
type DeepKeyOf<T> = {
[K in keyof T]: K extends string | number ? K : DeepKeyOf<T[K]>;
};
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = DeepKeyOf<User>; // "name" | "age" | "address.city" | "address.postalCode"
メリット:
- ライブラリ不要
- シンプルで分かりやすい
- 型エイリアスの再帰的な定義が必要
- ネストが深い場合、コードが冗長になる
Paths
型を使用する
import { Paths } from "ts-toolbelt";
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = Paths<User>; // "name" | "age" | "address.city" | "address.postalCode"
- コードが簡潔になる
- ライブラリのインストールが必要
deep-keyof
ライブラリを使用する
import { deepKeyOf } from "deep-keyof";
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = deepKeyOf<User>; // "name" | "age" | "address.city" | "address.postalCode"
type DeepKeyOf<T> = {
[K in keyof T]: K extends string | number ? K : DeepKeyOf<T[K]>;
};
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = DeepKeyOf<User>; // "name" | "age" | "address.city" | "address.postalCode"
const user: User = {
name: "John Doe",
age: 30,
address: {
city: "New York",
postalCode: 10001,
},
};
// DeepKeys 型を使用して、ユーザーの深いプロパティにアクセスできます
console.log(user.name); // "John Doe"
console.log(user.address.city); // "New York"
console.log(user["address.postalCode"]); // 10001
import { Paths } from "ts-toolbelt";
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = Paths<User>; // "name" | "age" | "address.city" | "address.postalCode"
const user: User = {
name: "John Doe",
age: 30,
address: {
city: "New York",
postalCode: 10001,
},
};
// Paths 型を使用して、ユーザーの深いプロパティにアクセスできます
console.log(user.name); // "John Doe"
console.log(user.address.city); // "New York"
console.log(user["address.postalCode"]); // 10001
import { deepKeyOf } from "deep-keyof";
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = deepKeyOf<User>; // "name" | "age" | "address.city" | "address.postalCode"
const user: User = {
name: "John Doe",
age: 30,
address: {
city: "New York",
postalCode: 10001,
},
};
// deep-keyof ライブラリを使用して、ユーザーの深いプロパティにアクセスできます
console.log(user.name); // "John Doe"
console.log(user.address.city); // "New York"
console.log(user["address.postalCode"]); // 10001
type DeepKeyOf<T> = T extends object
? {
[K in keyof T]: K extends string | number
? K | `${K}.${DeepKeyOf<T[K]>}`
: never;
}
: never;
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = DeepKeyOf<User>; // "name" | "age" | "address.city" | "address.postalCode"
const user: User = {
name: "John Doe",
age: 30,
address: {
city: "New York",
postalCode: 10001,
},
};
// reduce 関数を使用して、ユーザーの深いプロパティにアクセスできます
console.log(user.name); // "John Doe"
console.log(user.address.city); // "New York"
console.log(user["address.postalCode"]); // 10001
独自の型ガードを使用する
type DeepKeyOf<T> = T extends object
? {
[K in keyof T]: K extends string | number
? K | `${K}.${DeepKeyOf<T[K]>}`
: never;
}
: never;
type IsObject<T> = T extends object ? true : false;
type DeepKeyOf<T> = IsObject<T> extends true
? {
[K in keyof T]: K extends string | number
? K | `${K}.${DeepKeyOf<T[K]>}`
: never;
}
: never;
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = DeepKeyOf<User>; // "name" | "age" | "address.city" | "address.postalCode"
const user: User = {
name: "John Doe",
age: 30,
address: {
city: "New York",
postalCode: 10001,
},
};
// 独自の型ガードを使用して、ユーザーの深いプロパティにアクセスできます
console.log(user.name); // "John Doe"
console.log(user.address.city); // "New York"
console.log(user["address.postalCode"]); // 10001
@ts-toolbelt/deep-keys ライブラリを使用する
import { DeepKeys } from "@ts-toolbelt/deep-keys";
type User = {
name: string;
age: number;
address: {
city: string;
postalCode: number;
};
};
type DeepKeys = DeepKeys<User>; // "name" | "age" | "address.city" | "address.postalCode"
const user: User = {
name: "John Doe",
age: 30,
address: {
city: "New York",
postalCode: 10001,
},
};
// @ts-toolbelt/deep-keys ライブラリを使用して、ユーザーの深いプロパティにアクセスできます
console.log(user.name); // "John Doe"
console.log(user.address.city); // "New York"
console.log(user["address.postalCode"]); // 10001
typescript