Búsqueda de sitios web

Comportamiento extraño de tipos en TypeScript al usar extends en genéricos


interface empty{

}
type IfExtends<T, B, Y, N> = T extends B ? Y : N
let a: IfExtends<keyof empty, never, number, string> /// type never
let b: keyof empty extends never?number:string       /// type number

En mi opinión, el tipo genérico IfExtends es igual al tipo de la declaración extends de b. Pero el tipo de a es never y b es number (extends true). ¿Por qué?

Respuesta #1

Esto es el resultado de aplicar un tipo condicional distributivo a never. Si se aplica un tipo condicional distributivo a una unión, el resultado será una unión de aplicar ese tipo condicional a cada constituyente de la unión:

type IfExtends<T> =  T extends number ? "Y" : "N"
type A = IfExtends<number | string> // = IfExtends<number> | IfExtends<string> = "Y" | "N"

Entonces, ¿qué tiene que ver esto con never? Podemos obtener una pista si intentamos unir never con cualquier otro tipo:

type X = never | number // just number, never is omitted

never es la unión vacía, es decir, la unión sin ningún componente. Agregarlo a cualquier otra unión hará que desaparezca debido a esto.

Debido a que es la unión vacía, al distribuirse sobre ella, el tipo condicional en realidad nunca se evalúa, ya que no hay elementos en la unión a los que aplicarlo, y simplemente obtenemos never independientemente de cualquier condición en el tipo condicional

Usar la versión integrada real no produce el mismo resultado ya que la distributividad solo ocurre sobre parámetros de tipo desnudos, T es un parámetro de tipo desnudo, keyof empty no lo es.

Puedes desactivar la distributividad encapsulando T en un tipo tuple:

type IfExtends<T, B, Y, N> = [T] extends [B] ? Y : N
let a: IfExtends<keyof empty, never, number, string> /// also numberkeyof empty 

Enlace del parque