Last active
May 4, 2023 16:41
-
-
Save ahuggins-nhs/826906a58e4c1e59306bc0792e7826d1 to your computer and use it in GitHub Desktop.
Deeply omit members of an interface or type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Taken from https://stackoverflow.com/questions/55539387/deep-omit-with-typescript | |
/** Union of primitives to skip with deep omit utilities. */ | |
type Primitive = string | Function | number | boolean | Symbol | undefined | null | |
/** Deeply omit members of an array of interface or array of type. */ | |
export type DeepOmitArray<T extends any[], K> = { | |
[P in keyof T]: DeepOmit<T[P], K> | |
} | |
/** Deeply omit members of an interface or type. */ | |
export type DeepOmit<T, K> = T extends Primitive ? T : { | |
[P in Exclude<keyof T, K>]: //extra level of indirection needed to trigger homomorhic behavior | |
T[P] extends infer TP ? // distribute over unions | |
TP extends Primitive ? TP : // leave primitives and functions alone | |
TP extends any[] ? DeepOmitArray<TP, K> : // Array special handling | |
DeepOmit<TP, K> | |
: never | |
} | |
/** Deeply omit members of an array of interface or array of type, making all members optional. */ | |
export type PartialDeepOmitArray<T extends any[], K> = Partial<{ | |
[P in Partial<keyof T>]: Partial<PartialDeepOmit<T[P], K>> | |
}> | |
/** Deeply omit members of an interface or type, making all members optional. */ | |
export type PartialDeepOmit<T, K> = T extends Primitive ? T : Partial<{ | |
[P in Exclude<keyof T, K>]: //extra level of indirection needed to trigger homomorhic behavior | |
T[P] extends infer TP ? // distribute over unions | |
TP extends Primitive ? TP : // leave primitives and functions alone | |
TP extends any[] ? PartialDeepOmitArray<TP, K> : // Array special handling | |
Partial<PartialDeepOmit<TP, K>> | |
: never | |
}> |
This can be simplified a bit:
/** Union of primitives to skip with deep omit utilities. */
type Primitive = string | Function | number | boolean | symbol | undefined | null;
/** Deeply omit members of an interface or type. */
type DeepOmit<T, K extends keyof any> = T extends Primitive
? T
: T extends any[]
? { [P in keyof T]: DeepOmit<T[P], K> }
: {
[P in Exclude<keyof T, K>]: T[P] extends infer TP // Distribute over unions
? DeepOmit<TP, K>
: never;
};
Thanks, this is really useful when removing __typename
from apollo graphql! I had an issue with optionals though so I extended it as follows:
/** Union of primitives to skip with deep omit utilities. */
type Primitive =
| string
| Function
| number
| boolean
| Symbol
| undefined
| null;
/** Key Retrieval */
export type KeysOfType<T, U> = {
[K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
export type RequiredKeys<T> = Exclude<
KeysOfType<T, Exclude<T[keyof T], undefined>>,
undefined
>;
export type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>;
/** Deeply omit members of an array of interface or array of type. */
export type DeepOmitArray<T extends any[], K> = {
[P in keyof T]: DeepOmit<T[P], K>;
};
/** Deeply omit members of an interface or type. */
export type DeepOmit<T, K> = T extends Primitive
? T
: {
[P in Exclude<RequiredKeys<T>, K>]: T[P] extends infer TP // extra level of indirection needed to trigger homomorhic behavior // distribute over unions
? TP extends Primitive
? TP // leave primitives and functions alone
: TP extends any[]
? DeepOmitArray<TP, K> // Array special handling
: DeepOmit<TP, K>
: never;
} & {
[P in Exclude<OptionalKeys<T>, K>]?: T[P] extends infer TP // extra level of indirection needed to trigger homomorhic behavior // distribute over unions
? TP extends Primitive
? TP // leave primitives and functions alone
: TP extends any[]
? DeepOmitArray<TP, K> // Array special handling
: DeepOmit<TP, K>
: never;
};
/** Deeply omit members of an array of interface or array of type, making all members optional. */
export type PartialDeepOmitArray<T extends any[], K> = Partial<{
[P in Partial<keyof T>]: Partial<PartialDeepOmit<T[P], K>>;
}>;
/** Deeply omit members of an interface or type, making all members optional. */
export type PartialDeepOmit<T, K> = T extends Primitive
? T
: Partial<{
[P in Exclude<keyof T, K>]: T[P] extends infer TP // extra level of indirection needed to trigger homomorhic behavior // distribute over unions
? TP extends Primitive
? TP // leave primitives and functions alone
: TP extends any[]
? PartialDeepOmitArray<TP, K> // Array special handling
: Partial<PartialDeepOmit<TP, K>>
: never;
`}>;`
Maybe there is a better way that I'd be interested to hear but this works for me
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@armenr its actually not trhats bad really, it looks scary but its just a bunch of ternay operators over types (basically just if-thens in type code)