¿Cuál es el significado del tipo de parámetro `never[]` en el Manual de TypeScript?
En la sección sobre Inferencia dentro de los tipos condicionales, proporcionan un ejemplo:
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return
? Return
: never;
type Num = GetReturnType<() => number>;
type Str = GetReturnType<(x: string) => string>;
type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>;
¿Pero por qué usaron never[] aquí?
Respuesta #1
El punto de
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return
? Return
: never;
es obtener el tipo de retorno de cualquier función, y no rechazar firmas de llamadas basadas en sus tipos de parámetros. Cualquier firma de llamada que pases como Type debería ser asignable a (...args: never[]) => infer Return. Es decir, (...args: never[]) => infer Return está destinado a ser un supertipo de todas las firmas de llamadas; debería ser un tipo superior para las funciones. Consulta la Alternativa para "cualquier función" ((...args: any[]) => any) sin usar any para más discusión sobre esto.
Ahora, dado que en realidad estás desechando los parámetros de Type y en realidad no estás llamando a una función de tipo Type, hay varias formas de escribir un tipo de función principal en TypeScript que funcionarían para este propósito. Aquí hay una lista de ellas en lo que yo consideraría un orden descendente de preferencia:
(...args: never) => infer Return(...args: any) => infer Return(...args: any[]) => infer Return(...args: never[]) => infer Return
Los tipos de función son contravariantes en sus tipos de parámetros (véase Diferencia entre Variancia, Covarianza, Contravarianza, Bivariancia e Invarianza en TypeScript). Eso significa que si quieres que tu tipo de función coincida con más tipos de función, los parámetros de ese tipo deben coincidir con menos tipos de parámetros. Cada string es un string | number, lo que significa que cada (x: string | number)=>void es un (x: string)=>void y no al revés. Las uniones de funciones terminan pareciendo que toman intersecciones de parámetros (véase las notas de la versión TS3.3 para llamar a uniones de funciones). Si estás buscando un tipo superior para funciones (conceptualmente una unión de todos los tipos de función), necesitas un tipo inferior para los parámetros (conceptualmente una intersección de todos los tipos de parámetros).
Entonces, ¿cuál es la intersección de todas las posibles listas de parámetros? ¿Qué tipo se puede asignar a cualquier tupla posible como [a: string] y [] y [a: number, b: string], etc.? Bueno, el tipo never se puede asignar a todos los tipos. Es el tipo bottom de TypeScript. Y eso significa que (...args: never) => infer Return es el tipo de función top más correcto desde una perspectiva de seguridad de tipos.
También podrías usar el tipo any para este propósito, como (...args: any) => infer Return. El tipo any es intencionalmente inseguro y actúa como un tipo superior y un tipo inferior. En los casos en que realmente no te importe un tipo y solo quieras prevenir errores, puedes usar any. Así que (...args: any) => infer Return también es perfectamente aceptable si solo estás infiriendo Return.
También podrías usar (...args: any[]) => infer Return, ya que any[] coincidirá con cualquier tipo de arreglo posible.
Luego está (...args: never[]) => infer Return. El tipo never[] significa "un arreglo en el que no puedes indexar". Es un subtipo de casi todos los tipos de arreglos, pero no es exactamente la intersección de todos los tipos de arreglos. Todavía coincide con casi todas las funciones, pero debería comportarse de manera diferente cuando intentas llamarla. Una función del tipo (...args: never) => ? debería ser completamente inllamable, mientras que una del tipo (...args: never[]) => ? debería ser llamable con cero argumentos. En realidad, TypeScript trata estos tipos como esencialmente idénticos, lo cual es técnicamente incorrecto pero en la práctica no causa muchos problemas. Ve microsoft/TypeScript#48840 para errores interesantes y rarezas relacionadas con estos tipos.
Pero, no estamos llamando a la función en absoluto aquí, así que esto es mayormente académico. Para los propósitos de GetReturnType, never[] es tan bueno como never.
