'How to have a conditional function based on another argument

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.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source