Cannot assign type which should be compatible when conditional generics are used

Viewed 57

I have stumbled upon issue with TypeScript types where I can't assign narrower type to more broad one when generics with conditions are present. However, all the generic paramters are fully instantied so TypeScript should be able to resolve both types fully and compare them based on types of each property.

This is simplified version of what I was trying to do which throws the error:

// Returns keyes that have type extending Types
type KeysMatching<T, Types> = { [K in keyof T]: T[K] extends Types ? K : never }[keyof T];

type Mapping = {
    STR: string;
    NUM: number;
    BOOL: boolean;
}

type Values = Mapping[keyof Mapping];

type Generic<T extends Values> = {
    typeStr: KeysMatching<Mapping, T>
}

const foo: Generic<boolean> = { typeStr: 'BOOL' };
const bar: Generic<Values> = foo;
// Type 'Generic<boolean>' is not assignable to type 'Generic<Values>'.
//  Type 'Values' is not assignable to type 'boolean'.
//    Type 'string' is not assignable to type 'boolean'.(2322)

The sample is also available in TypeScript playground

I have found this answer on SO which states

If a conditional type contains an unresolved type parameter (such as T) typescript will not try to reason much about the conditional type.

however, all of the conditional types should be resolved here.

I don't get why TypeScript first says 'Generic<boolean>' is not assignable to type 'Generic<Values>' and then it switches it around that 'Values' is not assignable to type 'boolean', but I am trying to assign boolean to Values.

Can anybody explain why TypeScript throws this error?

Edit:

If I remove the condition from KeysMatching type, the assignment does not throw error so it seems like it's connected with the condition, but all the generic types should be resolved when checking assignability of the type.

I tried it with more simple case including conditions and it doesn't throw error:

type Conditional<T, V> = T extends V ? T : unknown;
const foo2: Conditional<string, Values> = 'string';
const bar2: Conditional<Values, Values> = foo2;
1 Answers

As I understand it, the first error message references the assignment of foo to bar, whereas foo is of type Generic<boolean> while bar is of type Generic<Values> which is not an acceptable assignment.

The second message tries to explain why this is not an acceptable assignment, after you already narrowed down the type to Generic<boolean> you're trying to assign it to a variable which now accepts a broader type definition, hence the error - So the third message tells you that TS already knows that foo's Generic type can't be string | number, it has to be boolean, therefore string can't be assigned to boolean

You could "remove" the type narrowing with something like const bar: Generic<Values> = foo as Generic<Values>; which wouldn't cause an error.

Maybe this clears it up a bit

Related