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
{{ message }}
Repository navigation
Generic inference inside of pipe is incorrect when strict mode is disabled #25826
classC1{P1: string;}classC2{P2: string;}classC3{P3: string;}functiona(value: C1|null): string;functiona(value: C2|null): string;functiona(value: C1|C2|null): string{return"";}varc=(value?: C1|C2|C3|null|undefined): string=>{if(valueinstanceofC1||valueinstanceofC2)returna(value);// C1 | C2 --> not null or undefined, there is no reasons to give an error...// is a(C1) a valid call? YES// is a(C2) a valid call? YES// why error? returnvalue ? value.P3 : "";}varb=(value?: C1|C2|C3|null|undefined): string=>{if(valueinstanceofC1)returna(value);// C1if(valueinstanceofC2)returna(value);// C2returnvalue ? value.P3 : "";}
Gabriele (@dardino) This issue is concerning overloads when used with a pipe function, which doesn't appear to be the case with your issue. Perhaps you could open a new issue to avoid conflating the issue being tracked here?
In the call to pipe we first make inferences from the first and third arguments because they are not "contextually sensitive" (i.e. they're not function expressions with parameters that have contextual types), and then finally from the contextually sensitive second argument. However, as we are inferring from the third argument, we end up fixing (i.e. freezing) inferences for T2 so that we can instantiate the generic optionOf in the context of (x: T2) => T3. That causes T2 to become {} (because we have no candidates for it). We then finally end up with an error because string | undefined isn't assignable to {} in --strictNullChecks mode.
We might explore doing better in situations where instantiation of a generic function argument in the context of a function type parameter causes fixing of type parameters and attempt to delay inference from such arguments further. That would be what the "Suggestion" label refers to.
After a fairly long discussion of options here, we don't have any ideas for how to fix this without breaking other scenarios. Full unification is of course a "solution" but that's basically a complete ground-up rewrite, so not really in the cards for the time being.
Thanks for the explanations. I keep running into issues with pipe and I'm not sure if they are the same or different to this one. See also #25637 and #25791.
Is there a good rule of of thumb we can use with pipe to avoid running into issues like this?
declareconstpipe: {// Workaround 1: enable this overload// <A, B, C>(ab: (a: A) => B, bc: (b: B) => C): (a: A) => C;<A,B,C,D>(ab: (a: A)=>B,bc: (b: B)=>C,cd: (c: C)=>D): (a: A)=>D;};declareconstgetString: ()=>string;declareconstorUndefined: (name: string)=>string|undefined;declareconstidentity: <T>(value: T)=>T;constfn=pipe(getString,/* Unexpected type error: Type 'string | undefined' is not assignable to type '{}'. Type 'undefined' is not assignable to type '{}'. */string=>orUndefined(string),// Workaround 2: pass the function directly, instead of wrapping:// get,identity,);
Activity
dardino commentedon Jul 21, 2018
from #25637 :
OliverJAsh commentedon Jul 23, 2018
Gabriele (@dardino) This issue is concerning overloads when used with a pipe function, which doesn't appear to be the case with your issue. Perhaps you could open a new issue to avoid conflating the issue being tracked here?
dardino commentedon Jul 23, 2018
Ok sorry
OliverJAsh commentedon Aug 8, 2018
Mohamed Hegazy (@mhegazy) How come this is marked as a suggestion? Isn't this a bug?
ahejlsberg commentedon Aug 9, 2018
Oliver Joseph Ash (@OliverJAsh) It's an effect (or limitation) of our type inference algorithm.
In the call to
pipewe first make inferences from the first and third arguments because they are not "contextually sensitive" (i.e. they're not function expressions with parameters that have contextual types), and then finally from the contextually sensitive second argument. However, as we are inferring from the third argument, we end up fixing (i.e. freezing) inferences forT2so that we can instantiate the genericoptionOfin the context of(x: T2) => T3. That causesT2to become{}(because we have no candidates for it). We then finally end up with an error becausestring | undefinedisn't assignable to{}in--strictNullChecksmode.We might explore doing better in situations where instantiation of a generic function argument in the context of a function type parameter causes fixing of type parameters and attempt to delay inference from such arguments further. That would be what the "Suggestion" label refers to.
RyanCavanaugh commentedon Aug 17, 2018
After a fairly long discussion of options here, we don't have any ideas for how to fix this without breaking other scenarios. Full unification is of course a "solution" but that's basically a complete ground-up rewrite, so not really in the cards for the time being.
OliverJAsh commentedon Aug 21, 2018
Thanks for the explanations. I keep running into issues with
pipeand I'm not sure if they are the same or different to this one. See also #25637 and #25791.Is there a good rule of of thumb we can use with
pipeto avoid running into issues like this?OliverJAsh commentedon Feb 14, 2019
Simpler test case for this:
As mentioned in #29904.