如何为含类式属性的JS函数正确添加TypeScript类型?
正确为带属性的函数定义TypeScript类型
这种在函数上挂载额外属性的场景确实容易让TS新手困惑——毕竟我们通常习惯函数就是函数、对象就是对象,但JS里函数本身也是对象,TS完全可以优雅地处理这种情况,不用反复依赖as类型断言。
第一步:定义函数+属性的交叉类型
首先,我们需要创建一个交叉类型,把函数的调用签名和它的额外属性合并在一起,完整描述wait的类型:
// 完整描述wait函数的类型:既是调用函数,又可选带有reject方法 type WaitFunction = ((ms: number) => Promise<void>) & { reject?: (reason: string) => void; };
这里用&把两部分类型合并:前半部分是wait作为函数的调用签名,后半部分是它携带的可选reject属性的类型。
第二步:用类型声明变量并实现逻辑
接下来直接用这个类型声明wait变量,实现时只需要做一次类型断言(比你之前多次零散断言优雅得多),之后所有属性访问都会被TS正确推断:
const wait: WaitFunction = ((ms: number) => { return new Promise<void>((resolve, reject) => { const timeoutId = setTimeout(() => { // 直接访问wait.reject,TS能识别这个属性的存在 delete wait.reject; resolve(); }, ms); // 直接赋值reject属性,类型完全匹配,无需额外断言 wait.reject = (reason: string) => { clearTimeout(timeoutId); reject(reason); }; }); }) as WaitFunction;
第三步:复用类型处理多个类似函数
如果你的代码库中有大量这类“带额外属性的函数”,可以定义一个通用类型模板,避免重复编写相似类型:
// 通用模板:给任意返回Promise的函数添加reject属性 type FunctionWithReject<T extends (...args: any[]) => Promise<any>> = T & { reject?: (reason: string) => void; }; // 比如另一个类似的sleep函数,直接复用模板 type SleepFunction = FunctionWithReject<(ms: number) => Promise<void>>; const sleep: SleepFunction = ((ms: number) => { // 实现逻辑和wait类似... return new Promise(resolve => setTimeout(resolve, ms)); }) as SleepFunction;
这个方案的优势
- 避免零散的
as断言,类型定义集中清晰,可读性更强 - 所有类型检查由TS自动完成,减少手动断言带来的潜在错误
- 通用类型模板可以复用在多个类似函数上,符合代码复用原则
内容的提问来源于stack exchange,提问作者Cal Courtney
相关产品推荐
相关产品推荐

