为何这段代码从TypeScript 4.7版本起无法编译?
问题:TypeScript 4.7+中双参数简洁箭头函数作为事件监听器编译失败
问题描述
一段在TypeScript 4.6中可正常编译的代码,从4.7版本(含4.7、4.8、4.9)开始编译报错。测试发现两种可规避问题的场景:
- 监听器不使用直接返回表达式的简洁箭头函数,改用带
{}的块级函数体 - 箭头函数仅接收一个参数而非两个
相关代码示例:
import { EventEmitter } from 'node:events'; // 改编自discord.js,许可证 Apache 2.0 interface ClientEvents { warn: [message: string]; shardDisconnect: [closeEvent: CloseEvent, shardId: number]; } class BaseClient extends EventEmitter { public constructor() { super(); }; } type Awaitable<T> = PromiseLike<T> | T; declare class Client extends BaseClient { public on<K extends keyof ClientEvents>(event: K, listener: (...args: ClientEvents[K]) => Awaitable<void>): this; public on<S extends string | symbol>( event: Exclude<S, keyof ClientEvents>, listener: (...args: any[]) => Awaitable<void>, ): this; } // 演示代码 const bot = new Client(); // 编译失败:event被误判为string | CloseEvent联合类型 bot.on("shardDisconnect", (event, shard) => console.log(`Shard ${shard} disconnected (${event.code},${event.wasClean}): ${event.reason}`)); // 编译成功:改用块级函数体 bot.on("shardDisconnect", (event, shard) => { console.log(`Shard ${shard} disconnected (${event.code},${event.wasClean}): ${event.reason}`); }); // 编译成功:仅使用单个参数 bot.on("shardDisconnect", event => console.log(`${event.code} ${event.wasClean} ${event.reason}`))
问题原因
这是TypeScript 4.7对函数参数推断逻辑调整后引发的类型匹配歧义。当使用双参数的简洁箭头函数时,TypeScript的类型推断会错误匹配到on方法的第二个重载(用于处理未在ClientEvents中定义的事件),导致第一个参数event被推断为string | CloseEvent的联合类型,进而触发属性访问的类型错误。
而改用块级函数体或单参数箭头函数时,类型推断能够正确匹配到第一个重载(对应预定义的shardDisconnect事件),因此类型检查正常通过。
解决方案
可以选择以下任意一种方式修复该问题:
1. 改用块级函数体
将简洁箭头函数替换为带{}的块级函数体,即使函数内只有一行代码:
bot.on("shardDisconnect", (event, shard) => { console.log(`Shard ${shard} disconnected (${event.code},${event.wasClean}): ${event.reason}`); });
2. 显式标注参数类型
通过显式指定参数类型,帮助TypeScript正确识别对应的重载:
bot.on("shardDisconnect", (event: CloseEvent, shard: number) => console.log(`Shard ${shard} disconnected (${event.code},${event.wasClean}): ${event.reason}`) );
3. 使用单参数箭头函数(业务允许时)
如果业务逻辑仅需要第一个参数,保持单参数的简洁箭头函数形式即可:
bot.on("shardDisconnect", event => console.log(`${event.code} ${event.wasClean} ${event.reason}`) );
内容的提问来源于stack exchange,提问作者Kevin Lu
相关产品推荐
相关产品推荐

