I wrote the following type guard definition:
function is_zero_args<T, F extends (...args: any[]) => T>(func: F): func is () => T {
return func.length === 0;
}
Logically, this should work - but TypeScript fails the compilation with the following error:
A type predicate's type must be assignable to its parameter's type.
Type '() => T' is not assignable to type 'F'.
'() => T' is assignable to the constraint of type 'F', but 'F' could be instantiated with a different subtype of constraint '(...args: any[]) => T'.
I know that F
could be created with different args - that's what I'm guarding against.
You don't need/want F
to be a type parameter there. Callers are allowed to supply a specific type argument for F
to which () => T
may not be assignable (e.g. is_zero_args<string, (x: string) => string>(f)
). The last line of your error message explains this ("F
could be instantiated with a different subtype").
Here's another example of the same kind of error without involving function types:
function is_blah<S extends string>(str: S): str is 'blah' {
// ^^^^^^
// A type predicate's type must be assignable to its parameter's type.
// Type 'string' is not assignable to type 'S'.
// 'string' is assignable to the constraint of type 'S', but 'S' could be instantiated with a different subtype of constraint 'string'.
return str === 'blah';
}
Usually when a generic function's type parameter only has a single usage in the call signature you can remove that type parameter and instead inline its constraint. Indeed, that works here:
function is_zero_args<T>(func: (...args: any[]) => T): func is () => T {
return func.length === 0;
}