I'm trying to compile this Typescript snippet:
function foo(v: string) { return 'foo'; }
function bar(v: string | number) { return 'bar'; }
const notCallable: typeof foo | typeof bar = function() {} as any;
// Fails type check, even though all the unioned functions accept string.
notCallable('a');
The compiler infers the type of notCallable
as ((v: string) => string) | ((v: string | number) => string)
, which looks fine, but is not considered callable:
Cannot invoke an expression whose type lacks a call signature. Type '((v: string) => string) | ((v: string | number) => string)' has no compatible call signatures.
Note that if the parameter lists match, it works fine, even if the return types differ.
function foo(v: string) { return 'foo'; }
function bar(v: string) { return 0; }
const callable: typeof foo | typeof bar = function() {} as any;
// Passes type check, presumably because the parameter lists match exactly (even though they have different return types).
callable('a');
This example is a simplified case that I originally discovered while trying to describe the notion of "continuous numeric D3 scale functions", which I had tried to define as:
import { ScaleContinuousNumeric, ScaleTime } from 'd3-scale';
type ValidScale = ScaleContinuousNumeric<number, number> | ScaleTime<number, number>;
const s: ValidScale = function() {} as any;
// No good, the only valid overload for `domain` is the no-argument one, even though both interfaces have one that accepts a superset of `Array<number>`.
s.domain([ 0, 1 ]);
Is there a construct that would allow me to express this without having to write a simpler interface that both ScaleContinuousNumeric
and ScaleTime
are assignable to?
This is unintended behaviour that appears for versions of TypeScript up til 3.3 where it was fixed. It is also mentioned in the changelog for TypeScript 3.3:
Improved behavior for calling union types
In prior versions of TypeScript, unions of callable types could only be invoked if they had identical parameter lists.
...
In TypeScript 3.3, this is no longer an error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With