Búsqueda de sitios web

typescript - Alternativa a "cualquier función" ((...args: any[]) => any) sin usar any


Me pregunto si existe una manera alternativa de especificar el tipo de función "cualquier función" que no use any (y por lo tanto mejore la seguridad de tipos). Para aclarar, estoy buscando una alternativa para el siguiente tipo:

type AnyFunctionAsUsual = (...args: any[]) => any;

Pude reemplazar el valor de retorno con unknown, pero todavía hay un any en la siguiente definición:

type AnyFunctionReturningUnknown = (...args: any[]) => unknown;

Hasta donde sé, los tipos AnyFunctionAsUsual y AnyFunctionReturningUnknown pueden tomar los mismos valores. Y también creo que el último es suficientemente seguro en cuanto a tipos (porque el any para los parámetros no desactiva ninguna comprobación de tipo, hasta donde yo sé).

Pero a eslint no le gusta ya que todavía viola la regla no-explicit-any, que está incluida en plugin:@typescript-eslint/recommended.

¿Hay alguna manera de definir un tipo que tome los mismos valores que los dos tipos anteriores y también haga feliz al linter? (Donde desactivar el linter o la regla no es lo mismo que hacer feliz al linter.)

Respuesta #1

Quieres que SomeFunction sea un tipo superior para funciones, donde si tienes una función f puedas asignarla a una variable de tipo SomeFunction, y si tienes un x que no es función, entonces no puedes asignarlo a SomeFunction. Es como el tipo unknown para funciones. (No puedes usar unknown porque acepta no funciones.)


Tus tipos (...args: any) => unknown o (...args: any[]) => unknown cumplen con estos criterios, pero no son seguros en cuanto a tipos porque usan el inseguro tipo any. En un sistema de tipos seguro, hay un compromiso donde los valores de los tipos superiores son fáciles de proporcionar pero difíciles de consumir. Si quiero un valor de tipo unknown, puedes darme lo que quieras. Pero una vez que lo tengo, no sé qué hacer con él sin inspeccionarlo más a fondo. Si quiero un valor de tipo SomeFunction, puedes darme cualquier función que quieras. Entonces, una vez que lo tengo, podría hacer cualquier cosa que funcione para una función arbitraria, como inspeccionar su propiedad length o... eh... bueno, no hay mucho que pueda hacer con ella. No puedo llamarla, porque no sé qué argumentos aceptaría. Así que consumir un SomeFunction será difícil, o al menos complicado. El tipo (...args: any) => unknown te permite llamarla como quieras, incluso si esto conduciría inmediatamente a un error en tiempo de ejecución:

type SomeFunction = (...args: any) => unknown;
const f: SomeFunction = (x: string) => x.toUpperCase();
f(); // accepted erroneously, runtime error!
f(1, 2, 3); // accepted erroneously, runtime error!

Esa es la razón por la que no deberías usar any allí.


Suponiendo que quieras una versión segura de SomeFunction, entonces debemos asegurarnos de que la lista de argumentos no tenga un tipo de "vale todo". De hecho, debería ser lo contrario. No deberías poder llamarla en absoluto. Eso se debe a que los tipos de función son contravariantes en sus tipos de parámetros (ver Diferencia entre Variancia, Covariancia, Contravariancia y Bivarianza en TypeScript y una descripción de la opción de compilador --strictFunctionTypes para más información). En TypeScript, eso significa que el tipo de función superior debería tener un tipo inferior para su lista de parámetros. Y en TypeScript, el tipo inferior es el tipo never:

type SomeFunction = (...args: never) => unknown;
const f: SomeFunction = (x: string) => x.toUpperCase();
f(); // error in TS5.0 and above
f(1, 2, 3); // error

Esta capacidad de (...args: never) => unknown para actuar como un tipo superior de función fue implementada principalmente en microsoft/TypeScript#35438 y completada en microsoft/TypeScript#52387, la cual será lanzada con TypeScript 5.0. Antes de eso, es posible llamar a f() arriba sin error (aunque no sea seguro), pero otras llamadas son rechazadas.

Enlace de Playground al código

Artículos relacionados