TypeScript如何定义变换管道中相互关联的递归Gasket数组类型
问题原因
你遇到的报错是TypeScript递归类型的使用限制导致的:
- 3.7引入的递归类型别名不支持直接将带未推断泛型参数的递归类型放在剩余元组的展开位置,你定义的
Gaskets<I,O,Via>中Via属于未固定的类型变量,TS无法自动推导该泛型的实际类型,因此触发循环引用报错。 - 你手动展开的方案相当于把
Via的所有可能推导路径都显式列了出来,避开了自动推导的问题,但扩展性很差。
解决方案
不用手动展开,通过递归条件类型+infer自动推导gasket链的输入输出类型即可,完整实现如下:
首先保留基础类型定义:
interface Sink<E> { seal():void; send(value:E):void; } type Gasket<I,O> = (target:Sink<O>) => Sink<I>;
然后定义工具类型用于推导合法gasket链的输入类型:
/** * 从末尾输出类型O倒推gasket数组的开头输入类型 * @param O 整条链最终的输出类型(即最后Sink的接收类型) * @param T 所有gasket组成的数组类型 */ type ChainInput<O, T extends unknown[]> = T extends [Gasket<infer I, O>] ? I : T extends [Gasket<infer I, infer Mid>, ...infer Rest] ? ChainInput<Mid, Rest> : never;
接着定义Join函数的重载和实现:
// 重载1:仅传入单个Sink直接返回 export function Join<I>(sink: Sink<I>): Sink<I>; // 重载2:传入任意长度的gasket序列 + 末尾的Sink export function Join<T extends unknown[], O>(...args: [...gaskets: T, sink: Sink<O>]): Sink<ChainInput<O, T>>; // 实现 export function Join(...args: unknown[]): Sink<unknown> { // 从后往前组装流水线 let current = args.at(-1) as Sink<unknown>; for (let i = args.length - 2; i >= 0; i--) { const gasket = args[i] as Gasket<unknown, unknown>; current = gasket(current); } return current; }
效果验证
你的测试用例可以直接使用,TS会自动推导整条链的输入输出类型:
chain.send()的参数会被推断为string,符合预期receiver的入参也会被推断为string,类型错误会直接抛出- 支持任意长度的gasket链,不需要手动展开枚举
内容的提问来源于stack exchange,提问作者Wyrframe
相关产品推荐
相关产品推荐

