Búsqueda de sitios web

typescript - No se puede asignar un tipo que debería ser compatible cuando se usan genéricos condicionales


Me he topado con un problema con los tipos de TypeScript donde no puedo asignar un tipo más específico a uno más general cuando hay genéricos con condiciones presentes. Sin embargo, todos los parámetros genéricos están completamente instanciados, por lo que TypeScript debería ser capaz de resolver ambos tipos por completo y compararlos basándose en los tipos de cada propiedad.

Esta es una versión simplificada de lo que estaba tratando de hacer, lo cual genera el 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)

La muestra también está disponible en el playground de TypeScript

He encontrado esta respuesta en SO que dice

Si un tipo condicional contiene un parámetro de tipo no resuelto (como T), TypeScript no intentará razonar mucho sobre el tipo condicional.

sin embargo, todos los tipos condicionales deberían resolverse aquí.

No entiendo por qué TypeScript primero dice 'Generic<boolean>' no es asignable al tipo 'Generic<Values>' y luego lo invierte diciendo que 'Values' no es asignable al tipo 'boolean', pero estoy intentando asignar boolean a Values.

¿Alguien puede explicar por qué TypeScript lanza este error?

Editar:

Si quito la condición del tipo KeysMatching, la asignación no lanza un error, así que parece que está relacionada con la condición, pero todos los tipos genéricos deberían resolverse al verificar la asignabilidad del tipo.

Lo intenté con un caso más simple incluyendo condiciones y no arroja error:

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

Respuesta #1

Según entiendo, el primer mensaje de error hace referencia a la asignación de foo a bar, mientras que foo es del tipo Generic<boolean> mientras que bar es del tipo Generic<Values>, lo cual no es una asignación aceptable.

El segundo mensaje intenta explicar por qué esta no es una asignación aceptable, después de que ya hayas reducido el tipo a Generic<boolean> estás intentando asignarlo a una variable que ahora acepta una definición de tipo más amplia, de ahí el error. Así que el tercer mensaje te dice que TS ya sabe que el tipo Genérico de foo no puede ser string | number, tiene que ser boolean, por lo tanto string no se puede asignar a boolean.

Podrías "eliminar" la restricción de tipo con algo como const bar: Generic<Values>=foo as Generic<Values>; lo cual no causaría un error.

Quizás esto lo aclare un poco 😅