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
changed the title [-]3.4 regression in flowing outer contextual types to inner contextually sensitive expressions(?)[/-][+]3.4 regression for type argument inference[/+]on Mar 14, 2019
clipboard.ts:2 return Promise.resolve(100).then(() => {
~~~~~~~
TS2345: Argument of type '() => Promise<{ success: false; }> | { success: true; }' is not assignable to parameter of type '(value: number) => { success: false; } | PromiseLike<{ success: false; }>'.
Type 'Promise<{ success: false; }> | { success: true; }' is not assignable to type '{ success: false; } | PromiseLike<{ success: false; }>'.
Type '{ success: true; }' is not assignable to type '{ success: false; } | PromiseLike<{ success: false; }>'.
Type '{ success: true; }' is not assignable to type '{ success: false; }'.
Types of property 'success' are incompatible.
Type 'true' is not assignable to type 'false'.
We now make inferences from generic return type positions (#29478), so the return statements have the contextual type { success: boolean } | PromiseLike<{ success: boolean }> where previously they had none. Because of the contextual type we infer more specific return expression types { success: true } and Promise<{ success: false }>, which in turn means we end up inferring both { success: true } and { success: false } for the TResult1 type variable of then. Neither of those is a subtype of the other, so we pick just one and subsequently end up with an error.
The reason is that a contextual type number doesn't cause us to infer literal types. Only when the contextual type itself contains literal types do we infer literal types. And that brings us to the core issue: For number and string we have a distinct non-literal type representing the entire domain, whereas boolean is indistinguishable from the union false | true (and the same is true for enum literal types).
We could attempt to track whether all occurrences of false and true in a contextual type originate in a reference to the boolean type and then not infer literal types, but it is non-trivial.
classBase{}classFoo<T>extendsBase{constructor(value: T,fn: (value: Foo<T>)=>void){super();}}constfoo=newFoo(5,(f: Foo<number>)=>{});// foo has type Foo<5>constbar=newFoo(5,(f: Foo<number>&Base)=>{});// bar has type Foo<number>
Activity
DanielRosenwasser commentedon Mar 14, 2019
CC Anders Hejlsberg (@ahejlsberg) Wesley Wigham (@weswigham) Ryan Cavanaugh (@RyanCavanaugh)
[-]3.4 regression in flowing outer contextual types to inner contextually sensitive expressions(?)[/-][+]3.4 regression for type argument inference[/+]DanielRosenwasser commentedon Mar 14, 2019
Matt Bierner (@mjbvz)
RyanCavanaugh commentedon Mar 14, 2019
Error is
weswigham commentedon Mar 14, 2019
Ah, yes, the common bug of us inferring literal types where we really shouldn't.
DanielRosenwasser commentedon Mar 14, 2019
(thanks, I had to run to catch a bus)
ahejlsberg commentedon Mar 27, 2019
We now make inferences from generic return type positions (#29478), so the
returnstatements have the contextual type{ success: boolean } | PromiseLike<{ success: boolean }>where previously they had none. Because of the contextual type we infer more specific return expression types{ success: true }andPromise<{ success: false }>, which in turn means we end up inferring both{ success: true }and{ success: false }for theTResult1type variable ofthen. Neither of those is a subtype of the other, so we pick just one and subsequently end up with an error.A simpler example of the same issue:
Interestingly, this very similar example doesn't have an issue:
The reason is that a contextual type
numberdoesn't cause us to infer literal types. Only when the contextual type itself contains literal types do we infer literal types. And that brings us to the core issue: Fornumberandstringwe have a distinct non-literal type representing the entire domain, whereasbooleanis indistinguishable from the unionfalse | true(and the same is true for enum literal types).We could attempt to track whether all occurrences of
falseandtruein a contextual type originate in a reference to thebooleantype and then not infer literal types, but it is non-trivial.16 remaining items
MikeJerred commentedon Feb 13, 2021
I'm not sure if this is the same issue or not: