You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何定义仅包含异步方法的字典的TypeScript超类型?

问题与解决方案

需求背景

我们有多个仅包含异步方法的API接口,示例如下:

interface MyApi {
  foo(a: string): Promise<number>;
  bar(a: number, b: boolean): Promise<string>;
  baz(): Promise<void>;
}

interface MyApi2 {
  qux(a: string): Promise<void>;
}

需要定义一个超类型约束,实现:

  • 所有仅包含异步方法的接口都能通过约束
  • 包含非异步成员(如{foo: number;}或{foo(): number})的接口会被拦截报错

现有方案的问题

带索引签名的类型(无法使用)

最初尝试定义带字符串索引签名的类型:

type Api = {
  [name: string]: (...args: any[]) => Promise<any>;
}; 

但用于泛型约束时会触发错误:

class Something<T extends Api> {
}

new Something<MyApi>(); // TS2344 错误

错误信息:

TS2344: Type 'MyApi' does not satisfy the constraint 'Api'.   
Index signature for type 'string' is missing in type 'MyApi'.

原因是具体接口没有声明通用字符串索引签名,无法匹配带索引的Api类型。

递归泛型约束(可行但繁琐)

后来实现了递归泛型约束的方案,虽然能满足需求,但写法不够简洁:

type Api<T> = {
  [P in keyof T]: (...args: any[]) => Promise<any>;
};

class Something<T extends Api<T>> {
}

该方案可以正常通过new Something<MyApi>(),也会对非异步接口(如{foo: number;})报错,但T extends Api<T>的递归写法不够直观。

更简洁的非递归解决方案

可以直接在泛型约束中使用映射类型,无需额外定义泛型Api<T>,写法更简洁直观:

class Something<T extends { [K in keyof T]: (...args: any[]) => Promise<any> }> {
}

效果验证

  • 合法异步接口:new Something<MyApi>() 正常通过类型检查
  • 非法非异步接口:new Something<{foo: number;}>() 触发预期错误:

    TS2344: Type '{ foo: number; }' does not satisfy the constraint '{ [K in keyof { foo: number; }]: (...args: any[]) => Promise; }'.   
    Types of property 'foo' are incompatible.     
    Type 'number' is not assignable to type '(...args: any[]) => Promise'.

可选优化:保留原方法类型(更严谨)

如果需要保留原方法的参数和返回值类型,而非用any模糊处理,可以调整约束为:

class Something<T extends { 
  [K in keyof T]: (...args: Parameters<T[K]>) => Promise<ReturnType<T[K]> extends Promise<infer R> ? R : never> 
}> {
}

这个版本会严格验证每个方法确实是返回Promise的异步函数,同时保留原方法的类型信息。


内容的提问来源于stack exchange,提问作者JHH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:55:25