-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Unexpected literal union type widening in generic type inference #32596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
For reference this is my original code (simplified version of my real scenario): function extended_example() {
type SomeUnion = "A" | "B" | "C";
interface SomeInterface<T extends SomeUnion> { prop: T }
const a: SomeInterface<"A"> = { prop: "A" }
const b: SomeInterface<"B"> = { prop: "B" }
function fun<T extends SomeUnion>(x: T, y: SomeInterface<T>) { }
fun("A", a); // no error
fun("A", b); // !!! no error because widening to "A" | "B"
fun("B", a); // !!! no error because widening to "A" | "B"
} ... here I can pass a wrong |
Somewhat unrelated. But what do you think of the following? const interfaceB: SomeInterface<"B"> = { prop: "B" }
function fun<T extends SomeUnion>(x: T, y: SomeInterface<T>) { }
declare const aOrB : "A"|"B";
//Could possibly be "A", interfaceB
fun(aOrB, interfaceB); |
Interesting. I thinks here it's okay that no error happens because there is no "out of bounds" type widening involved like my ( Also it is symmetric with non-literals ... type SomeUnion = string | number | boolean;
function fun<T extends SomeUnion>(x: T, y: T) { }
declare const strOrNum: string | number;
fun(strOrNum, 42); // no error as expected But this brings me to the following example which should error again (it's generalization of the original example): type SomeUnion = "A" | "B" | "C";
interface SomeInterface<T extends SomeUnion> { prop: T }
function fun<T extends SomeUnion>(x: T, y: SomeInterface<T>) { }
declare const aOrB : "A" | "B";
const interfaceC: SomeInterface<"C"> = { prop: "C" }
fun(aOrB, interfaceC); // !!! no error which again is asymmetric with non-literals: type SomeUnion = string | number | boolean;
function fun<T extends SomeUnion>(x: T, y: T) { }
declare const strOrNum: string | number;
fun(strOrNum, true); // error as expected |
I'm not on my computer but what happens when you try this, function fun (...args : ["A", Some<"A">]|["B", Some<"B">]) If the above works as you expected, how about, type Blah<T> = T extends any ? [T, Some<T>] : never
function fun (...args : Blah<SomeUnion>)
function fun2<ArgsT extends Blah<SomeUnion>> (...args : ArgsT) |
It's assumed that a union of two literal types from the same primitive family are an OK inference, but not from two different families. Any inference that matches the constraint and is a supertype of all candidates is legal and your function implementation should be assuming that this is possible - in the worst case, a caller of your function could write fun<"A" | "B">("A", "B"); See also #14829 You could write this instead: function extended_example() {
type SomeUnion = "A" | "B" | "C";
interface SomeInterface<T extends SomeUnion> { prop: T }
const a: SomeInterface<"A"> = { prop: "A" }
const b: SomeInterface<"B"> = { prop: "B" }
function fun<T extends SomeInterface<SomeUnion>>(x: T extends SomeInterface<infer U> ? U : never, y: T) { }
fun("A", a); // no error
fun("A", b); // errors
fun("B", a); // errors
} |
Okay. Thanks for info and sharing the workaround with the conditional type. ... and +1 for |
TypeScript Version: 3.5.1
Search Terms: type widening generic union inference
Code
Expected behavior:
Compile errors in the two "!!!" marked lines.
Actual behavior:
No compile error, instead the type inference inferred
"A" | "B"
forT
.That's pretty unexpected and IMO a bug, because for literal type parameters it makes no sense to infer any union, it obviously can only be one value (therefor the type widening should stop when seeing a parameter which is directly the generic literal union type).
Also it's asymmetric with non literal union types (see
counterexample
).Playground Link: Here
The text was updated successfully, but these errors were encountered: