Búsqueda de sitios web

typescript: Usar infer con nunca obtener un resultado diferente


Hice algunas demostraciones para reproducir mis problemas. Aquí están.

Pregunta1: ¿por qué el tipo g obtiene 2?

// 1
type f = [never] extends [infer S1, ...infer S2]
  ? ([S1] extends [never] ? 1 : 2)
  : 3
// why 2
type g = [never] extends [infer S1, ...infer S2]
  ? ([never] extends [S1] ? 1 : 2)
  : 3

Desde mi perspectiva, el tipo f es igual a g porque S1 es never.

Pregunta2: ¿por qué el tipo h obtiene 2?

// 1
type i = [never] extends [infer S1]
  ? ([never] extends [S1] ? 1 : 2)
  : 3
// why 2
type h = [never] extends [infer S1, ...infer S2]
  ? ([never] extends [S1] ? 1 : 2)
  : 3

Desde mi perspectiva, el tipo i equivale a h porque S2 no se utiliza.

Pregunta3: ¿por qué el tipo k obtiene never?

// 1
type j = [never] extends [infer S1, ...infer S2]
  ? (never extends S1 ? 1 : 2)
  : 3
// why never ?
type k = [never] extends [infer S1, ...infer S2]
  ? (S1 extends never ? 1 : 2)
  : 3

No pude entender ni un poco. Pensé que k debería ser al menos uno de 1 2 3.

Respuesta #1

Tus preguntas 1 y 2 parecen estar exponiendo un error en TypeScript.

También espero que tus tipos F, G, H e I (los tipos no primitivos se escriben convencionalmente en UpperCamelCase y no en lowerCamelCase, lo que hace que parezcan nombres de variables) evalúen a 1. El hecho de que G y H evalúen a 2 es sorprendente. Mi investigación indica que este comportamiento es nuevo; puedes verificar que tu código funciona como se espera en TypeScript 4.1.5, pero falla en TypeScript 4.2.2. Específicamente, este cambio ocurrió alrededor del 12 o 13 de enero de 2021. Tu código funciona como se espera en TypeScript 4.2.0-dev.20210112, pero falla en TypeScript 4.2.0-dev.20210113.

Parece muy probable que este comportamiento se introdujera cuando microsoft/TypeScript#42248 se fusionó en la rama principal del código. Esta solicitud de extracción solucionó un error (microsoft/TypeScript#39992) pero parece haber causado algunos errores nuevos, como microsoft/TypeScript#42331.

He presentado microsoft/TypeScrpit#43213 para hacer un seguimiento de este problema. Por ahora diría que si tienes un caso de uso real que necesita ser construido como tu G y H anteriores, y si no puedes esperar a que se solucione este problema, entonces puedes solucionarlo usando un alias de tipo en lugar del never simple, así:

type N = never
type Gfixed = [N] extends [infer S1, ...infer S2] ? ([N] extends [S1] ? 1 : 2) : 3 // 1

Tu pregunta 3 se está comportando como se esperaba. En el tipo K, (S1 extends never ? 1 : 2) es un tipo condicional distributivo porque el tipo que se está verificando, S1, es un parámetro de tipo. Y una función de tipo así se distribuye sobre uniones; lo que significa que si F<T> es distributivo, entonces F<X | Y> será equivalente a F<X> | F<Y>.

Esto implica que F<never> debería ser never (o al menos que sería un subtipo de todas las posibles respuestas para F<T>). ¿Por qué? Porque X | never es equivalente a X, por lo que F<X | never> debe ser equivalente a F<X> | F<never>. Esto significa que para todo X, F<X> | F<never> es equivalente a F<X>. Conceptualmente, entonces, F<never> debería ser never.

Y resulta que el compilador de hecho trata a never como "la unión vacía" cuando se usa en un tipo condicional distributivo. Este comportamiento definitivamente me sorprendió cuando me encontré con él por primera vez, porque requiere un cierto cambio de mentalidad pensar en never como algún tipo de unión.

Observe :

type Distributive<T> = T extends string ? { a: T } : { b: T };

type X = Distributive<"a"> // {a: "a"}
type Y = Distributive<0> // {b: 0}
type Z = Distributive<"a" | 0> // {a: "a"} | {b: 0}

type StillX = Distributive<"a" | never> // {a: "a"}
type Never = Distributive<never> // never

Enlace de Playground al código