Home > Software engineering >  TS - types - omit field in parameter object in function while keeping types
TS - types - omit field in parameter object in function while keeping types

Time:04-26

I have a type that omits specified fields in functions parameter object like this:

type OmitFields<F, K extends string> = F extends (props: infer P) => infer R ? (props: Omit<P, K>) => R : never;

And use it like:

type Omitted = OmitFields<typeof functionWithObjectParam, 'first' | 'second'>

How can I make type parameter K be aware of type that props object gets inferred as, and restrict correct strings to its keys?

CodePudding user response:

I think you want something like:

type OmitFields<
    F extends (props: any) => any,
    K extends keyof Parameters<F>[0],
> = (props: Omit<Parameters<F>[0], K>) => ReturnType<F>

Here F is constrained to be a function that takes one argument. And K is constrained to the keys of that argument.

Then you can reconstruct the function type without any fancy infer stuff.

This seems to do what you want:

function functionWithObjectParam(props: { a: number, b: number }) {}

type Omitted = OmitFields<typeof functionWithObjectParam, 'b'> // ok
type OmittedBad = OmitFields<typeof functionWithObjectParam, 'bad'> // Type '"bad"' does not satisfy the constraint '"b" | "a"'.(2344)

declare const fn: Omitted

fn({ a: 1 }) // ok

fn({ a: 1, b: 2 })
// Argument of type '{ a: number; b: number; }' is not assignable to parameter of type 'Omit<{ a: number; b: number; }, "b">'.
//   Object literal may only specify known properties, and 'b' does not exist in type 'Omit<{ a: number; b: number; }, "b">'.

Playground

CodePudding user response:

You can use the TypeScript utility type Parameters instead of inferring the type of your F function 1st argument (props).

Constructs a tuple type from the types used in the parameters of a function type Type.

From there, you can directly be more specific to constrain your K generic, instead of just extends string: for example K extends keyof Parameters<F>[0]

Now to be even closer to your example, we could make it so F can be something else than a function, in which case the conditions are slightly more complicated, but still do-able:

type OmitFields<
  F,
  K extends (
    F extends (...args: any) => any
      ? keyof Parameters<F>[0]
      : string
  )
> = F extends (...args: any) => infer R
  ? (props: Omit<Parameters<F>[0], K>) => R
  : never;

declare function functionWithObjectParam(props: { key1: number; second: boolean; }): string;

type Omitted = OmitFields<typeof functionWithObjectParam, 'first' | 'second'>
  // TS2344 Type '"first" | "second"' does not satisfy the constraint '"second" | "key1"'. Type '"first"' is not assignable to type '"second" | "key1"'.

type Omitted2 = OmitFields<typeof functionWithObjectParam, 'key1' | 'second'> // Okay

type Omitted3 = OmitFields<string, 'first' | 'second'> // Okay

However I suspect that in your case the conditional type was only to enable type inference. In the case you can have a type that accepts only functions, it becomes much simpler:

type OmitFields2<
  F extends (...args: any) => any,
  K extends keyof Parameters<F>[0]
> = (props: Omit<Parameters<F>[0], K>) => ReturnType<F>

type Omitted4 = OmitFields2<typeof functionWithObjectParam, 'first' | 'second'>
  // TS2344 Type '"first" | "second"' does not satisfy the constraint '"second" | "key1"'.

type Omitted5 = OmitFields2<typeof functionWithObjectParam, 'key1' | 'second'> // Okay

type Omitted6 = OmitFields2<string, 'first' | 'second'>
  // TS2344 Type 'string' does not satisfy the constraint '(...args: any) => any'.

Demo

  • Related