You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constbaz: (n: number)=>number=(// param `n` is correctly inferred to be `number` :-)n=>n);constidentity=<T>(t: T)=>t;constbar: (n: number)=>number=identity(// param `n` is inferred as `any`, expected `number` :-(x=>x);
Expected behavior: TypeScript should infer the generic T in identity from the contextual type.
Actual behavior: TypeScript infers the generic T in identity as any!
// inferred bar 👉 (x: number) => numberconstbar=identity((x: number)=>x)
by doing
constbar: (n: number)=>number=identity(x=>x);
you're telling compiler that bar should be type of (n: number) => number, but your are assigning to it identity<any>((x:any): any => x) which is inferred to type (x:any) => any and therefore you got correct error
This is working as intended. Function and arrow expression parameters without type annotations are assigned contextual types only when the contextual type is known (before instantiation) to be a function type. In your example the contextual type is simply T, so we infer from the argument (x: any) => any to T. Had the contextual type been something like (x: T) => T, we would fix the inferences we've made so far for T (which would include inferences from the return type) and then assign that type to the x parameter.
To justify why I would like this with a real world use case:
constnumberPromise=Promise.resolve(1);numberPromise.then(// x is inferred as number, no type annotation requiredx=>x+1);// Later, when I need to expand on this function, if I use `pipe` I'm// forced to add a type annotation to the parameter when it wasn't// needed before.numberPromise.then(pipe(// x is inferred as any, requiring me to add a type// annotation to the param that wasn't needed beforex=>x+1,x=>x*2,));numberPromise.then(pipe((x: number)=>x+1,x=>x*2,));
The issue appears to be that the flowwall of overloads starts with a series of declarations that have zero parameters for the first function. In the process of matching against those, we assign type any to the x parameter of the lambda (because there is no matching contextual parameter). However, once we've made that assignment it sticks and for reasons of consistency we can't undo it. It all works if you eliminate all but the one-parameter-first-function declarations of flow. Not sure there is much we can do to change this.
Activity
Hotell commentedon Jun 28, 2018
hmm I don't think this is valid.
What you are after is following:
by doing
you're telling compiler that
barshould be type of(n: number) => number, but your are assigning to itidentity<any>((x:any): any => x)which is inferred to type(x:any) => anyand therefore you got correct errorOliverJAsh commentedon Jun 28, 2018
I thought TypeScript would use
bars type annotation as a "contextual type" and use it to infer the generic toidentityas(n: number) => number. 🤔Hotell commentedon Jun 28, 2018
I might be wrong but I don't think so, that would mean that Generics can flow deeply to function calls.
but again explicitly annotating a variable is expressing what type should be on the right side of assignment, not to infer
ahejlsberg commentedon Jun 28, 2018
This is working as intended. Function and arrow expression parameters without type annotations are assigned contextual types only when the contextual type is known (before instantiation) to be a function type. In your example the contextual type is simply
T, so we infer from the argument(x: any) => anytoT. Had the contextual type been something like(x: T) => T, we would fix the inferences we've made so far forT(which would include inferences from the return type) and then assign that type to thexparameter.Hotell commentedon Jun 28, 2018
yup what Anders Hejlsberg (@ahejlsberg) and (myself partially) said :)
OliverJAsh commentedon Jun 28, 2018
Thanks for the explanation Anders Hejlsberg (@ahejlsberg) and Martin Hochel (@Hotell).
To justify why I would like this with a real world use case:
ahejlsberg commentedon Jun 28, 2018
Oliver Joseph Ash (@OliverJAsh) How is
pipedefined?OliverJAsh commentedon Jun 28, 2018
Anders Hejlsberg (@ahejlsberg) I'm using
flowfrom Lodash, which is the same idea aspipe: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/64104e9760065bd498a3d62d98bb05ea26500dc2/types/lodash/common/util.d.ts#L209Hotell commentedon Jun 28, 2018
Hmm interesting, those definitions might be wrong ? this works
ahejlsberg commentedon Jun 29, 2018
The issue appears to be that the
flowwall of overloads starts with a series of declarations that have zero parameters for the first function. In the process of matching against those, we assign typeanyto thexparameter of the lambda (because there is no matching contextual parameter). However, once we've made that assignment it sticks and for reasons of consistency we can't undo it. It all works if you eliminate all but the one-parameter-first-function declarations offlow. Not sure there is much we can do to change this.OliverJAsh commentedon Jun 29, 2018
Ahh, that's good to know, so we can at least workaround it by patching the
flowtypes to remove that overload (which we don't use).I realised my original example was off the mark. Here is a correct example which does show the actual problem, as you described:
If you remove the first overload, the param
nis inferred as expected.OliverJAsh commentedon Jul 3, 2018
I guess this can be labelled as "won't fix"?