Given the following code:
type Function0<T> = () => T;
type Function1<T, A1> = (arg1: A1) => T;
type Function2<T, A1, A2> = (arg1: A1, arg2: A2) => T;
const foo: Function1<string, any> = () => "hi there";
I'd expect to get some sort of error, because I'm trying to assert some 0-argument function is a type that takes one argument.
However, the following compiles perfectly fine. Is there some way to type-check that these arities exactly match?
By default typescript assumes that a function with fewer parameters can be assign to where a the function will be called with more parameters, as the extra arguments will get ignored and no harm will come of it. A reasonable assumption:
const foo: Function1<string, any> = () => "hi there";
foo("Ignored, but why would that be a problem ?")
That being said, we can in some circumstances force the passed in function to have the same number of parameters as the expected number of parameters. This scenario involves passing the function to another function and using some conditional types to force an error if there are too few arguments:
type IsArg1Valid<T extends (...a: any) => any, E> = Parameters<T>['length'] extends 1 ? {} : E ;
function withFoo<T extends (arg: string) => any>(foo: T & IsArg1Valid<T, "You need to pass in a function with a first argument">){
}
withFoo(()=> {}) //Type '() => void' is not assignable to type '"You need to pass in a function with a first argument"'.
withFoo(a=> console.log(a)) //ok
withFoo((a, b)=> console.log(a))
Play
Note You should think long and hard if passing in a function with fewer parameters is truly an error, it should be harmless to do so in all circumstances at runtime. The only argument for it might be that the caller might overlook useful passed in parameters, but this might not justify forcing everyone to specify all paramters all the time
This
const foo: Function1<string, any> = () => "hi there";
is no different from a function that declares, but does not use its arguments:
const foo_anyway: Function1<string, any> = (arg1: any) => "hi there";
In Javascript, you will get no error if you call foo_anyway without arguments.
So it does not make sense to report errors about function values that are actually compatible with declared type.
On the other hand, if it needs more arguments, it becomes incompatible and the error is reported if you turn on --strictFunctionTypes
(compatibility rules for function types in TypeScript are even less strict by default, see function parameter bivariance)
const foo_error: Function1<string, any> = (arg1: any, arg2: string) => "hi there";
//Type '(arg1: any, arg2: string) => string' is not assignable
// to type 'Function1<string, any>'.
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