Buscando una forma de definir una función en TypeScript que acepte, como uno de sus argumentos, una función que puede tener diferentes firmas
const foo = (a: string, b: number) => { //do something } const bar = (a: number, b: string, c: boolean) => { //do something } const myDynamicFunction = ( str: string, // I tried this, but no dice method: ((a: string, b: number) => void) | ((a: number, b: string, c: boolean) => void), num: number, bool?: boolean ) => { if (bool) { method(num, str, bool) } else { method(str, num) } } myDynamicFunction( 'string', foo, 5 ) myDynamicFunction( 'string', bar, 5, true )Intenté ir por el agujero del conejo de "sobrecargas de funciones", pero eso me dejó donde comencé con esencialmente el mismo error de número inesperado de argumentos.
Creo que tendrías que lanzar tu método, porque TypeScript no puede resolver este:
if (bool) { (method as (a: number, b: string, c: boolean) => void)(num, str, bool) } else { (method as (a: string, b: number) => void)(str, num) }Hubo algunas sutilezas que no pude entender al principio, pero al desglosarlas finalmente me llevaron allí. Typescript parece tener algunas dificultades para eliminar la ambigüedad de los tipos de parámetros sin definir primero los alias de tipos discretos.
Necesitamos usar un predicado de tipo para determinar, según el valor de bool , si estamos tratando con una llamada a una función similar a BAR.
Cuando hacemos eso, todavía no hemos reducido el tipo de bool para que sea específicamente booleano (otro predicado también haría el trabajo), pero sabemos que ese es el caso, por lo que podemos usar el ! aserción no nula para permitir que se pase a un método que requiera boolean .
type FOO = (a: string, b: number) => void type BAR = (a: number, b: string, c: boolean) => void function isBAR(method: any, bool: boolean | undefined): method is BAR { return typeof method === 'function' && bool !== undefined } const foo = (a: string, b: number) => { //do something } const bar = (a: number, b: string, c: boolean) => { //do something } const myDynamicFunction = ( str: string, method: FOO | BAR, num: number, bool?: boolean ) => { isBAR(method, bool) ? method(num, str, bool!) : method(str, num) } myDynamicFunction('string', foo, 5) myDynamicFunction('string', bar, 5, true) myDynamicFunction('string', bar, 5, false)Mejorando mi respuesta anterior .
Esto tiene como objetivo proporcionar una verificación estática de que la función pasada a myDynamicFunction tiene una de las formas esperadas ( FOO o BAR ).
Tenga en cuenta que los métodos err[x] al final producen fallas de compilación, aunque con el mensaje un poco inútil "no se puede asignar a 'nunca'"
type FOO = (a: string, b: number) => void type BAR = (a: number, b: string, c: boolean) => void function isFOO(method: any, bool: boolean | undefined): method is FOO { return typeof method === 'function' && bool === undefined } function isBAR(method: any, bool: boolean | undefined): method is BAR { return typeof method === 'function' && bool !== undefined } const foo = (a: string, b: number) => { console.log('foo', a, b) } const bar = (a: number, b: string, c: boolean) => { console.log('bar', a, b, c) } const myDynamicFunction = <T extends FOO | BAR>( str: string, method: T & (Parameters<T> extends Parameters<FOO> ? T : Parameters<T> extends Parameters<BAR> ? T : never), num: number, bool?: boolean ) => { if (isBAR(method, bool)) { method(num, str, bool!) } else if (isFOO(method, bool)) { method(str, num) } else { throw "Fatal" } } type T0 = Parameters<FOO> myDynamicFunction('string', foo, 5) myDynamicFunction('string', bar, 5, true) myDynamicFunction('string', bar, 5, false) const err1 = (a: string, b: {}): void => {} myDynamicFunction('string', err1, 5, false) const err2 = (a: string): void => {} myDynamicFunction('string', err2, 5, false) const err3 = (): void => {} myDynamicFunction('string', err3, 5, false) const err4 = (a: number, b: string, c: {}): void => {} myDynamicFunction('string', err4, 5, false) const err5 = (a: number, b: string, c: boolean, d: any): void => {} myDynamicFunction('string', err5, 5, false)