Is it possible to maintain type coverage on a function that deeply removes all instances of a key in an object?
My function looks like this.
function omitDeep<T extends object>(obj: T, key: string): TWithoutProvidedKey {
return JSON.parse(
JSON.stringify(obj),
(key: string, value: any) => key === "__typename" ? undefined : value
);
}
Is there any way to make TWithoutProvidedKey
a reality?
The TypeScript Omit utility type Like the Pick type, the Omit can be used to modify an existing interface or type. However, this one works the other way around. It will remove the fields you defined. We want to remove the id field from our user object when we want to create a user.
To omit multiple keys from an object, pass a union of string literals in K .
TypeScript Records are a great way to ensure consistency when trying to implement more complex types of data. They enforce key values, and allow you to create custom interfaces for the values. The TypeScript Record type was implemented in TypeScript 2.1, and takes the form Record<K, T> .
The ReturnType in TypeScript is a utility type which is quite similar to the Parameters Type. It let's you take the return output of a function, and construct a type based off it.
The answers here were inspiring. I had some small issues with TypeScript 4.0 that I was able to work out. I'm maintaining it as a gist: https://gist.github.com/ahuggins-nhs/826906a58e4c1e59306bc0792e7826d1. Hope this helps some people, especially those wanting to deal with Partial utility in a deep omit.
/** 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 easily be done, you just need to use mapped types to recurse down the properties:
type Primitive = string | Function | number | boolean | Symbol | undefined | null
type DeepOmitHelper<T, K extends keyof T> = {
[P in 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
}
type DeepOmit<T, K> = T extends Primitive ? T : DeepOmitHelper<T,Exclude<keyof T, K>>
type DeepOmitArray<T extends any[], K> = {
[P in keyof T]: DeepOmit<T[P], K>
}
type Input = {
__typename: string,
a: string,
nested: {
__typename: string,
b: string
}
nestedArray: Array<{
__typename: string,
b: string
}>
nestedTuple: [{
__typename: string,
b: string
}]
}
type InputWithoutKey = DeepOmit<Input, '__typename'>
let s: InputWithoutKey = {
a: "",
nested: {
b:""
},
nestedArray: [
{b: ""}
],
nestedTuple: [
{ b: ""},
]
}
Just a caveat, this works on 3.4, the handling of mapped types on arrays and tuples has changed recently, so depending on version you might need to handle arrays as a special case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With