Repository navigation
Generic inference inside of pipe is incorrect when strict mode is disabled #25826
Description
Activity
from #25637 :
class C1 { P1: string; } class C2 { P2: string; } class C3 { P3: string; } function a(value: C1 | null): string; function a(value: C2 | null): string; function a(value: C1 | C2 | null): string { return ""; } var c = (value?: C1 | C2 | C3 | null | undefined): string => { if (value instanceof C1 || value instanceof C2) return a(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? return value ? value.P3 : ""; } var b = (value?: C1 | C2 | C3 | null | undefined): string => { if (value instanceof C1) return a(value); // C1 if (value instanceof C2) return a(value); // C2 return value ? value.P3 : ""; }
OliverJAsh commented
on Jul 23, 2018 ContributorAuthorMore actionsGabriele (@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?
Ok sorry
- addedSuggestionAn idea for TypeScriptAn idea for TypeScript
on Jul 23, 2018 OliverJAsh commented
on Aug 8, 2018 ContributorAuthorMore actionsMohamed Hegazy (@mhegazy) How come this is marked as a suggestion? Isn't this a bug?
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.
- addedNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.
on Aug 17, 2018 - addedDesign LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixedand removedNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.SuggestionAn idea for TypeScriptAn idea for TypeScript
on Aug 17, 2018 RyanCavanaugh commented
on Aug 17, 2018 MemberMore actionsAfter 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 commented
on Aug 21, 2018 ContributorAuthorMore actionsOliverJAsh commented
on Feb 14, 2019 ContributorAuthorMore actionsSimpler test case for this:
declare const pipe: { // 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; }; declare const getString: () => string; declare const orUndefined: (name: string) => string | undefined; declare const identity: <T>(value: T) => T; const fn = 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, );
As mentioned in #29904.
TypeScript Version: 2.9.2
Search Terms:
Code
With strict mode disabled:
Related Issues:
This is similar to—but not exactly the same as—other issues I've filed when trying to use
pipe:pipe#25637pipe#25791