ts-undefined-partial
Version:
TypeScript recursive conversion between optional (partial) and undefined properties.
55 lines • 2.5 kB
TypeScript
/**
* Turns properties of T which accept `undefined` to ?-optional. Basically, it
* adds "?" suffix to all properties which are undefined-assignable. Does it
* recursively for sub-properties (including arrays) and produces the most
* IntelliSense-clean typing possible (thus playing with never/unknown).
*
* Examples:
* ```
* UndefinedToPartial<{ a: { b: string | undefined }>
* -> { a: { b?: string | undefined } }
*
* UndefinedToPartial<{ a: Array<{ b: string | undefined } | undefined>>
* -> { a?: Array<{ b?: string | undefined }> | undefined }
* ```
*
* Notice that there is currently no way to generate `{ a?: string }` type in
* TS: it always adds `| undefined` alternative for ?-properties.
*/
export type UndefinedToPartial<T> = T extends [any, ...any[]] ? {
[K in keyof T]: UndefinedToPartial<T[K]>;
} : T extends unknown[] ? Array<UndefinedToPartial<T[number]>> : T extends object ? FunctionPropertyNames<T> extends never ? (ExcludeKeys<T, undefined> extends never ? unknown : {
[K in ExcludeKeys<T, undefined>]: UndefinedToPartial<T[K]>;
}) & (OnlyKeys<T, undefined> extends never ? unknown : {
[K in OnlyKeys<T, undefined>]?: UndefinedToPartial<T[K]>;
}) : T : T;
/**
* Converts `{ a?: T | undefined }` to `{ a: T | undefined }` (i.e. removes "?"
* keeping `undefined`). This is convenient for input values of some API when we
* want to make sure that, when we add a new optional API field, we don't forget
* to populate it with data. The tool never touches properties of objects with
* at least one method (e.g. it doesn't touch Date, Set, Map and any other class
* instances with methods).
*/
export type PartialToUndefined<T> = T extends [any, ...any[]] ? {
[K in keyof T]: PartialToUndefined<T[K]>;
} : T extends unknown[] ? Array<PartialToUndefined<T[number]>> : T extends object ? FunctionPropertyNames<T> extends never ? {
[K in keyof Required<T>]: PartialToUndefined<T[K]>;
} : T : T;
type FunctionPropertyNames<T> = T extends object ? T extends T ? {
[K in keyof T]-?: ((...args: any[]) => any) extends T[K] ? K : never;
}[keyof T] : never : never;
/**
* All of the keys of T to which U can be assigned.
*/
type OnlyKeys<T, U> = {
[K in keyof T]: U extends T[K] ? K : never;
}[keyof T];
/**
* All of the keys of T except those to which U can be assigned.
*/
type ExcludeKeys<T, U> = {
[K in keyof T]: U extends T[K] ? never : K;
}[keyof T];
export {};
//# sourceMappingURL=index.d.ts.map