为何TypeScript错误选择重载函数?mysql2 execute promisify问题
问题
手动promisify mysql2的execute方法时,TypeScript始终匹配最后一个重载,即便传入参数完全符合第一个重载的定义。
简化后的execute核心重载如下:
- 重载1:必填参数为
sql: string+values: any,可选回调函数 - 重载2:必填参数为
options: Record<string, unknown>,可选回调函数
调用promisify后的方法时,传入'sql', ['values']符合重载1的要求,却收到错误提示Expected 1 arguments, but got 2.,TypeScript强制要求使用仅需单个参数的重载2。
注:已知mysql2/promise但因客观原因无法使用。
原因分析
TypeScript处理重载函数的类型推断规则导致了该问题:
- 当重载函数作为参数传递给其他函数(如自定义的
promisify35)时,TypeScript不会保留所有重载签名,仅会将最后一个重载作为函数的整体类型进行推断。 promisify35的泛型约束T extends any[]会基于最后那个单参数重载推导类型,因此生成的promisified函数仅接受1个参数,自然拒绝传入2个参数的调用。
解决方案
要让TypeScript正确识别所有重载,可通过以下两种方式解决:
方案1:手动指定promisified函数的类型
直接为promisify后的execute声明匹配原重载的类型:
async function testbed() { const a = new (ExecutableBase())(); // 定义与原execute重载对应的promisified类型 type PromisifiedExecute = { <T>(sql: string, values: any): Promise<T>; <T>(options: Record<string, unknown>): Promise<T>; }; // 强制类型转换为自定义的重载类型 const promisifiedExecute = promisify35(a.execute) as PromisifiedExecute; // ✅ 可正确匹配第一个重载 await promisifiedExecute('sql', ['values']); }
方案2:增强promisify工具的重载支持
修改promisify35的类型定义,使其能够处理多重载函数:
// 针对双重载函数的promisify类型转换 type PromisifyOverload<T> = T extends { (...args: infer A1): void; (...args: infer A2): void; } ? ((...args: A1 extends [...infer P, infer _] ? P : never) => Promise<any>) & ((...args: A2 extends [...infer P, infer _] ? P : never) => Promise<any>) : never; function promisify35<T extends (...args: any[]) => void>( fn: T ): PromisifyOverload<T> { return ((...args: any[]) => { return new Promise((resolve, reject) => { fn(...args, (error: any, result: any) => { error ? reject(error) : resolve(result); }); }); }) as PromisifyOverload<T>; } async function testbed() { const a = new (ExecutableBase())(); // ✅ 自动匹配所有重载 await promisify35(a.execute)('sql', ['values']); await promisify35(a.execute)({ sql: 'SELECT * FROM table', values: [1] }); }
内容的提问来源于stack exchange,提问作者uPaymeiFixit
相关产品推荐
相关产品推荐

