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);
}
}
For pre 4.6 you can't destructure in the parameters, but you can still narrow: Playground Link