In this example:
type MyType = 'val1' | 'val2' | 'val3';
const variable = 'val1' as MyType;
const val2 = 'val2';
const val3 = 'val3';
declare function test<U extends MyType>(...args: U[]): void;
test(val2, val3); // U successfully resolves to "val3" | "val2"
declare function test2<T, U extends T>(value: T | undefined, ...values: U[]): void;
test2(variable, val2, val3); // U gets widened to "val1" | "val2" | "val3"
In both test
and test2
, U extends MyType
.
Then I expect test2
to resolve U
to "val3" | "val2"
just like it did in test
, but it doesn't.
Why is that?
CodePudding user response:
I believe this is a case of the type system cutting corners. Typescript is allowed to cut corners on the type checking every now and then. Especially if it can detect that the resulting type won't be used elsewhere in the type system. I think typescript recons that the type "val1" | "val2" | "val3"
is good enough for the arguments list.
However lets say that we change the return type of test2
from void
to U
. Then, since the type U
is expected to be returned and possibly used elsewhere in typescript, the compiler will need to dedicate more time to resolving the actual/most narrow type of U
.
type MyType = 'val1' | 'val2' | 'val3';
const variable = 'val1' as MyType;
const val2 = 'val2';
const val3 = 'val3';
declare function test2<T, U extends T>(value: T | undefined, ...values: U[]): void;
test2(variable, val2, val3); // U gets widened to "val1" | "val2" | "val3"
declare function test3<T, U extends T>(value: T | undefined, ...values: U[]): U;
test3(variable, val2, val3); // U successfully resolves to "val3" | "val2"