¿Por qué la gente de TypeScript creó la palabra clave infer
? Según los documentos , este es un ejemplo de cómo lo usaría:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
No entiendo por qué esto es necesario. ¿Por qué no puede ser simplemente:
type ReturnType<T> = T extends (...args: any[]) => R ? R : any;
¿Por qué no funciona esto? ¿Por qué es necesaria la palabra clave infer
?
Considere el siguiente código:
interface Example { foo: string } type GenericExample<T> = T extends Examlep ? 'foo' : 'bar';
Este código debería dar como resultado un error de compilación, porque Examlep
está mal escrito; no hay ningún tipo llamado Examlep
, y obviamente el programador quiso escribir Example
aquí.
Ahora imagine que la palabra clave infer
no es necesaria en una cláusula extends
de un tipo condicional. Entonces el código anterior no daría un error de compilación; vería que no hay ningún tipo llamado Examlep
, inferiría qué tipo es y luego (dado que Examlep
no tiene restricciones) observaría que T
sí extiende Examlep
para el tipo inferido.
En ese caso, GenericExample<T>
siempre sería 'foo'
independientemente de lo que sea T
, y no habría ningún error de compilación para informar al programador sobre el error. Esto sería lo incorrecto para el compilador, casi todo el tiempo.
Con infer
, el compilador se asegura de que haya declarado todas las variables de tipo explícitamente :
type MyType<T> = T extends infer R ? R : never; type T1 = MyType<{b: string}> // T1 is { b: string; }
Aquí declaramos una nueva variable de tipo R
en MyType
, que se deduce de T
.
( Tenga en cuenta que infer
siempre se usa dentro de la cláusula extends
de un tipo condicional ).
El uso de parámetros de tipo no declarados ahora puede generar un error de compilación:
type MyType2<T> = T extends R2 ? R2 : never; // error, R2 undeclared
Sin infer
, el compilador no sabría si desea introducir una variable de tipo adicional R2
que debe inferirse (consulte el primer caso), o si R2
es solo un error tipográfico/error tipográfico accidental. infer
existe para eliminar esta ambigüedad.
Más precisamente, el compilador verifica si T
es asignable a R
, cuando se omite infer
:
type R = { a: number } type MyType3<T> = T extends R ? R : never; // compare T with type R type T2 = MyType2<{b: string}> // T2 is never
Tenga en cuenta que infer R
de una declaración de tipo R
del mismo nombre:
type R = { a: number } type MyType<T> = T extends infer R ? R : never; type T1 = MyType<{b: string}> // { b: string; }
La palabra clave inferir le permite deducir un tipo de otro tipo dentro de un tipo condicional. Aquí hay un ejemplo:
type UnpackArrayType<T> = T extends (infer R)[] ? R: T; type t1 = UnpackArrayType<number[]>; // t1 is number
UnpackArrayType es un tipo condicional. Se lee como "Si T es un subtipo de (inferir R)[] , devuelve R. De lo contrario, devuelve T".
Para el alias de tipo t1, la condición en UnpackArrayType es verdadera porque número[] coincide con (inferir R)[]. Como resultado del proceso de inferencia, se infiere que la variable de tipo R es de tipo numérico y se devuelve desde la rama verdadera. Infer está ahí para decirle al compilador que se declara una nueva variable de tipo R dentro del alcance de UnpackArrayType.
type t2 = UnpackArrayType<string>; //t2 is string
Para t2, la condición en UnpackArrayType es falsa ya que el tipo de cadena no coincide con (inferir R)[], por lo que se devuelve como una cadena. Para más información, mira este artículo. https://javascript.plainenglish.io/typescript-infer-keyword-explained-76f4a7208cb0?sk=082cf733b7fc66228c1373ba63d83187
Lo estoy pensando así:
infer X
reemplaza any
.Para usar el ejemplo anterior,
type UnpackArrayType<T> = T extends any[] ? T[number]: T;
->
type UnpackArrayType<T> = T extends (infer R)[] ? R: T;
X
se declara como un nuevo tipo y se captura al mismo tiempo.X
ahora se puede usar en la parte true/false
del condicional.