I have a challenge in writing a utility type that takes an object as a generic parameter and a union of strings to recursively make specific properties optional. While it may sound simple, I encountered an issue that I need assistance with. Here is the utility type I tried:
export type DeepPartial<Type, Property> = Type extends Array<infer ArrayType>
? Array<DeepPartial<ArrayType, Property>>
: Type extends Record<string, unknown>
? {
[Key in Extract<keyof Type, Property>]?: DeepPartial<Type[Key], Property>;
} & {
[Key in Exclude<keyof Type, Property>]: DeepPartial<Type[Key], Property>;
}
: Type;
This works well, except for one scenario. If the passed type already has optional properties, it requires those properties to exist in the created type (with a possible value of undefined
), when they should actually not be required. For example:
type Test = {
a: boolean,
b: {
a: 1,
b: {
c?: string;
};
},
c?: string;
};
The variable defined below has an invalid type (when it shouldn't):
const d: DeepPartial<Test, 'a'> = {b: {b: {}}};
In order for it to work correctly, I need to explicitly provide an object with optional properties set to undefined:
const d: DeepPartial<Test, 'a'> = {b: {b: {c: undefined}}, c: undefined};
You can view this on TS Playground here: TS Playground Link