Home > database >  How to have a conditional function based on another argument
How to have a conditional function based on another argument

Time:03-05

I want to have a function that takes another function and the signature of that function changes depending on another argument passed to that function.

For example:

function doSomething<AllowNull extends boolean>({ allowNull, onChange }: { allowNull: AllowNull, onChange: AllowNull extends true ? (input: number | null) => void : (input: number) => void }) {
  if (allowNull) {
    onChange(null);
  } else {
    onChange(1);
  }
}

However Typescript complains that null can't be passed to onChange because it does not narrow the signature.

I tried this with function overloading too:

function doSomething(props: { allowNull: true, onChange: (input: number | null) => void }): void
function doSomething(props: { allowNull: false, onChange: (input: number) => void }): void
function doSomething({ allowNull, onChange }: { allowNull: boolean,onChange: (input: number | null) => void }): void {
  if (allowNull) {
    onChange(null);
  } else {
    onChange(1);
  }
}

But Typescript complains that one of the overloads signature is not compatible with the implementation.

How does one achieve this pattern in a type safe way?

Link to TS playground so you can see the errors.

CodePudding user response:

You can use a union in the the arguments. This will allow Typescript (4.6 and newer) to narrow onChange based on allowNull:


function doSomething({ allowNull, onChange }:
    | { allowNull: true, onChange: (input: number | null) => void }
    | { allowNull: false, onChange: (input: number) => void }): void {
    if (allowNull) {
        onChange(null);
    } else {
        onChange(1);
    }
}


Playground Link

For pre 4.6 you can't destructure in the parameters, but you can still narrow: Playground Link

  • Related