typescript - Inferencia de tipos para funciones pasadas a envoltorios de funciones genéricas como _.debounce
En TypeScript, si una expresión de función se pasa como argumento, los tipos de sus parámetros se infieren perfectamente:
var foo = (fn: (a: string) => void) => {};
foo(a => {
// a is inferred to be a string
alert(a.toLowerCase());
});
Es realmente conveniente para manejadores de eventos y otros callbacks. Sin embargo, si la expresión de función está envuelta en una llamada de una función envoltorio genérica, que toma una función como parámetro y devuelve una función con la misma firma (excepto por el valor de retorno), por ejemplo _.debounce de Lodash, la inferencia no ocurre.
var debounce = <T>(fn: (a: T) => void) => {
return (a: T) => { /* ... */ };
};
foo(debounce(a => {
// a is inferred to be {}
// type error: 'toLowerCase' doesn't exist on '{}'
alert(a.toLowerCase());
}));
Campo de Juegos TS
Como foo quiere que su argumento sea (a: string) => void, el compilador podría intentar encontrar un tipo así para T de manera que debounce<T> devolviera (a: string) => void. Pero no intenta hacer esto.
¿Me estoy perdiendo de algo? ¿Debería haber escrito las anotaciones de tipo para debounce de alguna otra manera? ¿Es por diseño? ¿Hay algún problema en GitHub sobre este caso?
ACTUALIZACIÓN-2017: ¡Ahora funciona! TS 2.5.
Respuesta #1
En primer lugar, cuando llamas a una función genérica sin argumentos de tipo, el compilador tiene que averiguar el tipo para cada parámetro de tipo.
Entonces, cuando llamas a debounce, el compilador tiene que encontrar candidatos para el tipo de T. Pero el único lugar de donde debounce puede extraer inferencias es tu función, y no proporcionaste un tipo explícito para tu parámetro de tipo.
Entonces el compilador determina que no tiene tipos con los que trabajar y recurre a {} (el "tipo vacío", a menudo llamado simplemente "llaves llaves") para T.
Todo ese proceso se llama inferencia de argumentos de tipo.
Lo que sucede entonces es que el compilador notará que no le has dado un tipo a los parámetros de tu función flecha. En lugar de usar por defecto any, determina que puede deducirlo a partir del tipo de los parámetros de debounce. Este proceso se llama tipado contextual.
Bueno, el tipo de fn es básicamente (a: {}) => void, así que el compilador piensa "¡está bien, genial, podemos darle un tipo a a!" lo cual termina siendo... {} desafortunadamente.
Así que, aunque esto no sea totalmente asombroso, la solución no es realmente tan mala. Solo agrega una anotación de tipo para a:
foo(debounce((a: string) => {
// Everything is fine!
alert(a.toLowerCase());
}));
o use un parámetro de tipo:
foo(debounce<string>(a => {
// Everything is fine!
alert(a.toLowerCase());
}));
