TypeScriptにおける「Recursive Partial」:ネストされたオブジェクト構造をオプション型にする方法
TypeScriptにおける「Recursive Partial<T>」の解説
TypeScript の Partial<T>
型は、すべてのプロパティをオプション型 (?
) に変換する型です。つまり、すべてのプロパティが必須ではなく、値が存在しない可能性があることを意味します。
一方、Recursive Partial<T>
型は、Partial<T>
型を再帰的に適用することで、ネストされたオブジェクト構造全体にオプション性を適用する型です。つまり、ネストされたすべてのプロパティもオプション型となり、値が存在しない可能性があることを意味します。
例
以下の例は、Recursive Partial<T>
型を使用して、ネストされたオブジェクト構造全体をオプション型にする方法を示しています。
type Person = {
name: string;
address: {
street: string;
city: string;
};
};
type OptionalPerson = RecursivePartial<Person>;
const optionalPerson: OptionalPerson = {
name: "John Doe",
address: {
city: "New York"
}
};
console.log(optionalPerson.name); // "John Doe"
console.log(optionalPerson.address.street); // undefined
この例では、Person
型は name
と address
というプロパティを持つオブジェクトを表します。address
プロパティはさらに street
と city
というプロパティを持つオブジェクトを表します。
Recursive Partial<Person>
型は、Person
型のすべてのプロパティをオプション型に変換します。つまり、optionalPerson
オブジェクトの name
プロパティは必須ですが、address
プロパティはオプションであり、street
プロパティはさらにオプションです。
用途
Recursive Partial<T>
型は、以下の用途に役立ちます。
- テストデータを作成する
- オブジェクト構造のデフォルト値を定義する
- オブジェクト構造の一部のみを更新する
- オプションで設定できる部分的なオブジェクト構造を定義する
注意点
Recursive Partial<T>
型を使用する際には、以下の点に注意する必要があります。
- TypeScript バージョン 4.1 以降でのみ使用可能
- すべてのプロパティが必須であると想定しているコードで使用すると、エラーが発生する可能性がある
- ネストされたすべてのプロパティがオプション型になるため、注意が必要
Recursive Partial<T>
型は、ネストされたオブジェクト構造全体にオプション性を適用する便利な型です。オプションで設定できる部分的なオブジェクト構造を定義したり、オブジェクト構造の一部のみを更新したりする際に役立ちます。
- Using recursive partial types in unit tests with TypeScript | by Sergio Mazzoleni | Medium: https://medium/@sergio.mazzoleni/using-recursive-partial-types-in-unit-tests-with-typescript-aad844416308
type Person = {
name: string;
address: {
street: string;
city: string;
};
};
type OptionalPerson = RecursivePartial<Person>;
const optionalPerson: OptionalPerson = {
name: "John Doe",
address: {
city: "New York"
}
};
console.log(optionalPerson.name); // "John Doe"
console.log(optionalPerson.address.street); // undefined
以下の例は、Recursive Partial<T>
型を使用して、オプションで設定できる部分的なオブジェクト構造を定義する方法を示しています。
type UserSettings = {
theme: string;
notificationSettings: {
emailNotifications: boolean;
pushNotifications: boolean;
};
};
type PartialUserSettings = RecursivePartial<UserSettings>;
const userSettings: PartialUserSettings = {
theme: "dark",
notificationSettings: {
emailNotifications: true
}
};
console.log(userSettings.theme); // "dark"
console.log(userSettings.notificationSettings.pushNotifications); // undefined
この例では、UserSettings
型は theme
と notificationSettings
というプロパティを持つオブジェクトを表します。notificationSettings
プロパティはさらに emailNotifications
と pushNotifications
というプロパティを持つオブジェクトを表します。
Recursive Partial<UserSettings>
型は、UserSettings
型のすべてのプロパティをオプション型に変換します。つまり、userSettings
オブジェクトの theme
プロパティは必須ですが、notificationSettings
プロパティはオプションであり、pushNotifications
プロパティはさらにオプションです。
type User = {
id: number;
name: string;
email: string;
};
const user: User = {
id: 1,
name: "John Doe",
email: "[email protected]"
};
const updatedUser: RecursivePartial<User> = {
name: "Jane Doe",
email: "[email protected]"
};
console.log(user.id); // 1
console.log(user.name); // "John Doe"
console.log(user.email); // "[email protected]"
console.log(updatedUser.id); // 1
console.log(updatedUser.name); // "Jane Doe"
console.log(updatedUser.email); // "[email protected]"
この例では、User
型は id
、name
、email
というプロパティを持つオブジェクトを表します。
updatedUser
オブジェクトは Recursive Partial<User>
型であり、name
と email
プロパティのみを更新しています。user
オブジェクトの id
プロパティは変更されませんが、name
と email
プロパティは updatedUser
オブジェクトの値に更新されます。
type UserSettings = {
theme: string = "light",
notificationSettings: {
emailNotifications: boolean = true,
pushNotifications: boolean = false
}
};
const userSettings: UserSettings = {};
console.log(userSettings.theme); // "light"
console.log(userSettings.notificationSettings.emailNotifications); // true
console.log(userSettings.notification
代替方法
- 手動で再帰的にオプション型を定義する
Recursive Partial<T>
型を使用せずに、手動で再帰的にオプション型を定義することができます。これは、より詳細な制御が必要な場合や、Recursive Partial<T>
型が動作しない場合に役立ちます。
type Person = {
name: string;
address: {
street: string;
city: string;
};
};
type OptionalPerson = {
name?: string;
address?: {
street?: string;
city?: string;
};
};
const optionalPerson: OptionalPerson = {
name: "John Doe",
address: {
city: "New York"
}
};
console.log(optionalPerson.name); // "John Doe"
console.log(optionalPerson.address.street); // undefined
Pick<T, K>
とOmit<T, K>
型を使用する
Pick<T, K>
型は、指定されたプロパティ (K
) のみを含む新しい型を作成します。Omit<T, K>
型は、指定されたプロパティ (K
) を除いたすべてのプロパティを含む新しい型を作成します。これらの型を使用して、オプション型のオブジェクト構造を作成することができます。
type Person = {
name: string;
address: {
street: string;
city: string;
};
};
type OptionalPerson = Pick<Person, "name"> & Omit<Person, "name" | "address.street">;
const optionalPerson: OptionalPerson = {
name: "John Doe"
};
console.log(optionalPerson.name); // "John Doe"
console.log(optionalPerson.address); // undefined
utility-types
ライブラリを使用する
utility-types
ライブラリは、Recursive Partial<T>
型と同様の機能を提供するいくつかの型を提供しています。
import { deepPartial } from "utility-types";
type Person = {
name: string;
address: {
street: string;
city: string;
};
};
type OptionalPerson = deepPartial<Person>;
const optionalPerson: OptionalPerson = {
name: "John Doe",
address: {
city: "New York"
}
};
console.log(optionalPerson.name); // "John Doe"
console.log(optionalPerson.address.street); // undefined
typescript