aocenas
aocenas

Reputation: 131

How to write omit function with proper types in typescript

What I ultimately want to do is to create a React HOC that would inject a property defined by a string and return a component without requiring the injected property. In essence though this boils down to a factory function returning an omit function.

As you can see in this example for some reason the type B ends up being 'never'.

const f = <A extends object, B extends keyof A>(arg: B) => (obj: A): Omit<A, B> => {
  delete obj[arg]
  return obj
}
// TS2345: Argument of type "test" is not assignable to parameter of type 'never'
const a = f('test')
const b = a({ test: 1})
const c = b.test

When I try keyof outside of generic parameter, it seem to be functioning better, but typescript does not infer the return type properly and I do not know how to type it as I do not know how to get the reference to the first string arg that could be used in Omit:

const f = <A extends object>(arg: keyof A) => (obj: A) => {
  delete obj[arg]
  return obj
}
const a = f('test')
const b = a({ test: 1 })
// Does not infer 'test' is no longer here
const c = b.test

For reference Omit is:

export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>

Upvotes: 1

Views: 3329

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249716

Since the first function has both type arguments, typescript will try to infer both when that call happens and since there is no inference site for A it will probably infer {} for it, making B never. The way to fix this, is to make B and when the second call occurs infer A, with the constraint that A must have a B key:

export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>

const f = <B extends keyof any>(arg: B) => <A extends Record<B, any>>(obj: A): Omit<A, B> => {
delete obj[arg]
return obj
}

const a = f('test')
const b = a({ test: 1, other: ""})
const c = b.test; // error as expected
const c2 = b.other; // ok 

Upvotes: 1

Related Questions