TypeScript中如何复用推断参数类型实现链式API避免重复?
解决TypeScript链式API的类型上下文积累问题
核心方案:用泛型跟踪已积累的类型
把Chain接口定义为带泛型参数的结构,泛型参数专门存储每次调用bravo时推断出的参数类型集合。每次调用bravo时,将新推断的类型追加到泛型集合中,并返回携带新泛型参数的Chain实例,以此实现类型上下文的递归传递,完全避免手动重复定义返回类型。
具体实现代码
// 泛型Accumulated存储已积累的类型,默认值为空数组 interface Chain<Accumulated extends unknown[] = []> { // bravo方法自动推断参数类型T,返回的Chain泛型参数追加T到原集合 bravo<T>(xs: T): Chain<[...Accumulated, T]>; // 可选:添加方法验证积累的类型是否正确 getAccumulatedTypes(): Accumulated; } // 实现Chain接口的具体类 class ChainImpl<Accumulated extends unknown[] = []> implements Chain<Accumulated> { bravo<T>(xs: T): Chain<[...Accumulated, T]> { return new ChainImpl<[...Accumulated, T]>() as Chain<[...Accumulated, T]>; } getAccumulatedTypes(): Accumulated { return [] as unknown as Accumulated; } } // 使用示例 const chain = new ChainImpl() .bravo(123) // 自动推断T为number,返回Chain<[number]> .bravo('hello') // 自动推断T为string,返回Chain<[number, string]> .bravo(true); // 自动推断T为boolean,返回Chain<[number, string, boolean]> // 验证类型:AccumulatedTypes会被推断为[number, string, boolean] type AccumulatedTypes = ReturnType<typeof chain.getAccumulatedTypes>;
关键细节说明
- 泛型参数
Accumulated:用数组类型存储每次调用的参数类型,通过扩展运算符[...Accumulated, T]实现类型的无缝追加,无需手动维护类型列表。 - 自动类型推断:每次调用
bravo时,TypeScript会自动识别参数xs的类型T,并自动更新返回的Chain泛型参数,全程无需手动指定类型。 - 代码简洁性:所有类型积累逻辑通过泛型递归传递实现,彻底告别重复写返回类型的冗余代码,可读性和可维护性大幅提升。
扩展:访问积累的类型做业务处理
如果链式API需要在后续方法中使用已积累的类型,可以直接借助泛型参数Accumulated:
interface Chain<Accumulated extends unknown[] = []> { bravo<T>(xs: T): Chain<[...Accumulated, T]>; // 用Accumulated类型约束回调参数 process(callback: (values: Accumulated) => void): void; } class ChainImpl<Accumulated extends unknown[] = []> implements Chain<Accumulated> { private accumulated: Accumulated = [] as unknown as Accumulated; bravo<T>(xs: T): Chain<[...Accumulated, T]> { const newAccumulated = [...this.accumulated, xs] as [...Accumulated, T]; const newChain = new ChainImpl<[...Accumulated, T]>(); newChain.accumulated = newAccumulated; return newChain; } process(callback: (values: Accumulated) => void): void { callback(this.accumulated); } } // 使用示例 new ChainImpl() .bravo(123) .bravo('hello') .process(values => { // values类型自动推断为[number, string],可直接调用对应类型的方法 console.log(values[0].toFixed(2)); console.log(values[1].toUpperCase()); });
内容的提问来源于stack exchange,提问作者Jason Kuhrt
相关产品推荐
相关产品推荐

