如何实现TypeScript方法调用后交替切换返回类型?
实现方法返回类型的交替切换
问题描述
能否让类的方法交替返回两种不同类型?示例如下:
const i = new Intersperse<number, string>(); i.next(); // 期望返回 number i.next(); // 期望返回 string i.next(); // 期望返回 number i.next(); // 期望返回 string
尝试用asserts仅能切换实例类型,但无法让方法返回值:
class Intersperse<Item, Separator, NextIsSeparator extends boolean = false> { next(): asserts this is Intersperse<Item, Separator, NextIsSeparator extends true ? false : true> { // 无法返回值 } } const i: Intersperse<number, string> = new Intersperse();
背景:正在复刻Rust的std::iter::Iterator::intersperse,可以将返回类型设为Item | Separator,但想尝试在类型层面实现严格的交替返回(尽管实用性不高)。
解决思路与实现
通过泛型状态标记+条件返回类型+this类型断言可以实现,核心是让next方法既返回对应类型的值,同时修改实例的泛型状态:
class Intersperse<Item, Separator, NextIsSeparator extends boolean = false> { private currentItemIndex = 0; private items: Item[]; private separator: Separator; private nextIsSeparator: NextIsSeparator = false as NextIsSeparator; constructor(items: Item[], separator: Separator) { this.items = items; this.separator = separator; } next(): NextIsSeparator extends false ? Item : Separator { if (!this.nextIsSeparator) { const result = this.items[this.currentItemIndex++] as ReturnType<this['next']>; // 切换下一次的返回状态 (this as unknown as Intersperse<Item, Separator, true>).nextIsSeparator = true as true; return result; } else { const result = this.separator as ReturnType<this['next']>; // 切换下一次的返回状态 (this as unknown as Intersperse<Item, Separator, false>).nextIsSeparator = false as false; return result; } } } // 使用示例 const i = new Intersperse([1, 2, 3], ","); const n1 = i.next(); // 类型为 number const s1 = i.next(); // 类型为 string const n2 = i.next(); // 类型为 number const s2 = i.next(); // 类型为 string const n3 = i.next(); // 类型为 number
关键细节
- 用
NextIsSeparator泛型标记下一次返回的类型状态,默认false表示首次返回Item next方法的返回类型通过条件类型NextIsSeparator extends false ? Item : Separator实现严格交替- 通过类型断言修改实例的泛型状态,让TypeScript识别下一次调用的返回类型
- 私有属性
nextIsSeparator跟踪运行时状态,保证逻辑与类型提示一致
替代方案(实用优先)
如果不需要极致的类型精度,直接返回Item | Separator是更简洁的选择,实际项目中实用性更高:
class Intersperse<Item, Separator> { private currentItemIndex = 0; private items: Item[]; private separator: Separator; private nextIsSeparator = false; constructor(items: Item[], separator: Separator) { this.items = items; this.separator = separator; } next(): Item | Separator { if (!this.nextIsSeparator) { this.nextIsSeparator = true; return this.items[this.currentItemIndex++]; } else { this.nextIsSeparator = false; return this.separator; } } }
内容的提问来源于stack exchange,提问作者k-tten
相关产品推荐
相关产品推荐

