Tratando de entender los límites de 'T extiende inferir U'

8

He entendido que algo así como:

type GenericExample<T> = T extends (infer U) ? U : 'bar';

es igual a:

type GenericExample<T> = T extends T ? T : 'bar';

Pero cuando las cosas se vuelven más elaboradas, TypeScript se queja:

type Types = 'text' | 'date' | 'articles' | 'params';

type MyExperiment<Type extends Types> =  { t : Type };

type MyExperimentsUnion = Types extends (infer U) ? MyExperiment<U> : never;
// Type 'U' does not satisfy the constraint 'Types'.
// Type 'U' is not assignable to type '"params"'.

Entonces, me gustaría preguntar por qué esto está mal: en este caso particular, la distribución sobre la unión debería tener lugar, por lo que el Utipo inferido debería ser text, datey así sucesivamente. Entonces, ¿qué significa T extends (infer U)realmente y cuándo sería apropiado usarlo?

Andrea Simone Costa
fuente

Respuestas:

0

No creo que esté destinado a usarse de la forma en que lo está utilizando, básicamente inferdebería usarse para "inferir" (o resolver tal vez un mejor nombre), más comúnmente de un genérico.

La forma en que lo está utilizando, está creando un tipo que no tiene ninguna parte "dinámica" (básicamente no es genérico), lo que significa que siempre es el mismo y, por lo tanto, inferir de algo que siempre es igual no tiene sentido . Porque en tiempo de compilación ya sabes que Typessolo se extiende Types & '...anything else', y dado que no puedes definir esa otra parte en tu MyExperimentsUniontipo, inferno tiene mucho uso.

Ejemplo de uso

interface Action<T> {
    payload: T
}

type ExtractGeneric<T> = T extends Action<infer X> ? X : never

function getPayload<T extends Action<any>>(action: T): ExtractGeneric<T> {
    return action.payload;
}

const myAction = { payload: 'Test' };
const myPayloadWithResolvedType = getPayload(myAction);

En el ejemplo anterior myPayloadWithResolvedTypetendría un stringtipo resuelto, porque si no estuviera usando infer, tendría que pasar ese tipo de retorno como segundo parámetro, probablemente así:

function getPayloadNonExtract<T extends Action<U>, U>(action: T): U {
    return action.payload;
}

Aquí está el enlace al patio de recreo.

Salud.

zhuber
fuente
1
Hola hombre, gracias por tu respuesta! Tengo que decir que conozco el propósito general de la inferpalabra clave, pero hice esta extraña pregunta porque descubrí que T extends (infer U)es válida en un tipo condicional, me sorprendió eso y comencé a investigar.
Andrea Simone Costa
1
Creo que entiendo su pregunta, pero no estoy seguro de cómo desea que resuelva el mecanografiado U. ¿Qué debería MyExperiment<U>ser? Quiero decir, Udebe ser "subclase" de Types, pero typecript no puede determinar qué es exactamente, Uya que no está pasando ningún tipo (no es tipo genérico), y no veo por qué debería ser predeterminado Types. Supongo que esa es una de las razones por las que obtienes el error, aunque estoy de acuerdo en que podría ser más descriptivo, pero parece que van con el estado de ánimo de que los desarrolladores deben saber que inferno se debe usar de esa manera.
zhuber
0

Probablemente no valga más de dos centavos pero:

type Types = 'text' | 'date' | 'articles' | 'params';
type MyExperiment<Type extends Types> =  { t : Type };

type MyExperimentsGenericUnion<T extends Types> = T extends (infer U) ? MyExperiment<U> : never;
// same error

type MyExperimentsUnionConstraint = any extends MyExperiment<infer U> ? MyExperiment<U> : never;
// no error

Se me ocurren algunas razones por las que MyExperimentsUnionConstraintfunciona pero MyExperimentsUnionno funciona, pero no tengo el conocimiento profundo del idioma para expresarlas de modo que tenga sentido.

Para dar una explicación con el ejemplo:

  • in MyExperimentsUnion, from Types extends U, Upodría inferirse como Types | 'bla'y luego MyExperiment<Type | 'bla'>no funciona.
  • en MyExperimentsUnionConstraint, Usolo se puede inferir como algo que funciona conMyExperiment
artcorpse
fuente