TypeScript中泛型作为泛型参数及Enumerable混入Vector的类型问题
TypeScript泛型与混入(Mixin)问题解答
以下针对你提出的四个问题逐一解答:
1. Error1:如何将泛型结果类型Collect<R>指定为Enumerable的参数?
TypeScript无法直接将“泛型类型”作为泛型参数传递,但可以通过泛型构造函数类型模拟该需求。先定义表示“可创建泛型实例的构造函数”的类型,再调整Enumerable的泛型参数约束:
// 定义泛型构造函数类型:接受任意泛型参数R,返回对应实例 type GenericConstructor = new <R>(...args: R[]) => any; // 调整Enumerable的泛型参数,Collect必须符合上述泛型构造函数类型 abstract class Enumerable<Collect extends GenericConstructor, T> { abstract [Symbol.iterator](): Iterator<T>; // 通过InstanceType和类型推断,获取Collect<R>的实例类型 abstract collect<R>(...args: R[]): InstanceType<Collect> extends { new <R>(...args: any[]): infer C } ? C : never; map<R>(op: (v: T) => R): ReturnType<this['collect']<R>> { return this.collect(...[...this].map(op)); } }
这样Collect即可表示泛型类,collect方法返回类型会自动推导为该类的泛型实例(如Vector<R>)。
2. Error2:Vector的collect方法返回类型用Vector<R>可行,但能否改为this<R>?
不行。this类型在TypeScript中表示当前实例的具体类型(如Vector<number>),而非泛型类本身,this<R>属于无效语法。
若想避免硬编码Vector<R>,可通过泛型构造函数的类型推断实现:
class Vector<T> { items: T[]; constructor(...items: T[]) { this.items = items; } *[Symbol.iterator](): IterableIterator<T> { for (const v of this.items) yield v; } // 用泛型构造函数类型推断返回类型 collect<R>(...items: R[]): InstanceType<typeof Vector> extends { new <R>(...args: any[]): infer C } ? C : never { return new (this.constructor as typeof Vector)(...items); } }
3. Error3:不使用any类型断言就无法识别构造函数签名,如何解决?
通过显式定义构造函数类型替代any断言,保留类型安全:
// 定义Vector的泛型构造函数类型 type VectorConstructor = new <R>(...args: R[]) => Vector<R>; class Vector<T> { items: T[]; constructor(...items: T[]) { this.items = items; } *[Symbol.iterator](): IterableIterator<T> { for (const v of this.items) yield v; } collect<R>(...items: R[]): Vector<R> { // 断言为预定义的VectorConstructor类型,而非any return new (this.constructor as VectorConstructor)(...items); } }
4. Question4:能否将interface Vector<T> extends Enumerable<Vector, T> {}整合到extend工具函数中?
无法完全整合。interface继承是编译时的静态类型操作,而extend是运行时的属性混入函数,TypeScript无法在运行时函数中自动修改编译时的接口定义。
不过可以通过泛型辅助类型减少重复代码:
// 定义辅助类型,自动合并Enumerable接口 type WithEnumerable<T extends GenericConstructor> = { new <R>(...args: R[]): InstanceType<T> & Enumerable<T, R>; }; // 使用时将原类转换为带Enumerable的类型 const VectorWithEnumerable = Vector as WithEnumerable<typeof Vector>; // 测试时直接使用转换后的类 const v = new VectorWithEnumerable(1, 2, 3); console.log([...v.map(v => v * 2)]);
若需混入多个Mixin,可扩展该辅助类型手动合并接口,但仍无法完全省略编译时的类型声明。
内容的提问来源于stack exchange,提问作者Alex Craft
相关产品推荐
相关产品推荐

