Home > Blockchain >  Generic types with an extends clause doesn't always get narrowed down like they should
Generic types with an extends clause doesn't always get narrowed down like they should

Time:12-06

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?

TypeScript playground

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"

playground

  • Related