I want to define a generic type ExcludeCart<T>
that is essentially T
but with a given key (in my case, cart
) removed. So, for instance, ExcludeCart<{foo: number, bar: string, cart: number}>
would be {foo: number, bar: string}
. Is there a way to do this in TypeScript?
Here's why I want to do this, in case I'm barking up the wrong tree: I'm converting an existing JavaScript codebase to TypeScript, which contains a decorator function called cartify
that takes a React component class Inner
and returns another component class Wrapper
.
Inner
should take a cart
prop, and zero or more other props. Wrapper
accepts a cartClient
prop (which is used to generate the cart
prop to pass to Inner
), and any prop that Inner
accepts, except cart
.
In other words, once I can figure out how to define ExcludeCart
, I want to do this with it:
function cartify<P extends {cart: any}>(Inner: ComponentClass<P>) : ComponentClass<ExcludeCart<P> & {cartClient: any}>
While there isn't a built-in subtraction type, you can currently hack it in:
type Sub0<
O extends string,
D extends string,
> = {[K in O]: (Record<D, never> & Record<string, K>)[K]}
type Sub<
O extends string,
D extends string,
// issue 16018
Foo extends Sub0<O, D> = Sub0<O, D>
> = Foo[O]
type Omit<
O,
D extends string,
// issue 16018
Foo extends Sub0<keyof O, D> = Sub0<keyof O, D>
> = Pick<O, Foo[keyof O]>
In the question's case, you would do:
type ExcludeCart<T> = Omit<T, 'cart'>
With TypeScript >= 2.6, you can simplify it to:
/**
* for literal unions
* @example Sub<'Y' | 'X', 'X'> // === 'Y'
*/
export type Sub<
O extends string,
D extends string
> = {[K in O]: (Record<D, never> & Record<string, K>)[K]}[O]
/**
* Remove the keys represented by the string union type D from the object type O.
*
* @example Omit<{a: number, b: string}, 'a'> // === {b: string}
* @example Omit<{a: number, b: string}, keyof {a: number}> // === {b: string}
*/
export type Omit<O, D extends string> = Pick<O, Sub<keyof O, D>>