Reputation: 13
In the following examples:
function functionA(x: string, y: number, z: SpecialType): void { }
const functionWrapper: (x, y, z) => functionA(x, y, z);
The parameters to functionWrapper are typed as any
. Is there some way to
encourage tsc to deduce their types from their usage? What limitation of the TypeScript type system prevents this from being resolved?
Upvotes: 0
Views: 180
Reputation: 20132
TypeScript type analysis work in top->down way. It means that it is equal to the program data flow where the data also is passed top->down. TypeScript is analyzing control-flow, but it is using for it informations given from the top, not from the bottom.
Consider such example of function:
function functionMaker(x: string, y: string) {
return () => x + y;
}
Above function return another function. There is none explicit typing in the definition of the anonymous function returned, but TS is able to analyse that functionMaker
always returns () => string
type of function.
Doing that another way around is not possible, as TS cannot predict what exactly you will do with the arguments. Consider below:
function functionA(x: string, y: number, z: SpecialType): void { }
function functionB(x: number): void { }
const functionWrapper: (x, y, z) => {
functionA(x, y, z); // x should be string
functionB(x); // x should be number
}
Now TS has two functions which are using one argument, but both of them have different requirement of the type. Any solving of this puzzle is wrong, as one or another function will fail.
In summary - type analysis is done top-down. But we can solve your issue by creating generic function which will wrap another.
function functionA(x: string, y: number, z: SpecialType): void { }
const wrap = <X, Y, Z, R>(f: (x: X, y: Y, z: Z) => R) => (x: X, y: Y, z: Z): R => f(x,y,z);
const functionWrapper = wrap(functionA);
Our wrap
is clearly defined as a wrapper, it means that its purpose is to infer the types from the given function and create another function which has the same arguments and the same return.
Upvotes: 1
Reputation: 95614
One reason: With your proposal, if Typescript could narrow the signature automatically, it would implicitly propagate across all call sites and cause those to fail.
function yourFunction(x) {
doSomethingThatTakesANumber(x);
}
// elsewhere
yourFunction(1);
// still elsewhere
yourFunction(-1);
Once you change yourFunction
to doSomethingThatTakesABoolean
, Typescript would infer that x
must be a boolean, and then the two unrelated calls would start failing. This would be particularly difficult to debug.
For the case of writing a wrapper function that takes the same signature as the function it wraps, you can use typeof functionA
to wrap the function without repeating yourself. Typescript can infer the types of the parameters from the type of the function, but not the code expressed in its body.
const functionWrapper2: typeof functionA = (x, y, z) => functionA(x, y, z);
Upvotes: 1