TypeScript是否有类似Java Class<T>的构造器类型及协变逆变声明?
在TypeScript中模拟Java的Class及实现协变/逆变
1. 定义Java风格的Class类型
TypeScript 没有内置的 Class<T> 类型,我们可以自定义构造函数类型来模拟这一特性,它表示能够实例化出 T 类型实例的类构造函数:
type Class<T> = new (...args: any[]) => T;
基于这个类型,你可以修正原有代码,让 Map 和方法参数严格约束为 A 类及其子类的构造函数,同时保证实例类型的匹配:
declare class A {} declare class B extends A {} class Foo { public static readonly bar: Map<Class<A>, A> = new Map(); public static getIt<T extends A>(clazz: Class<T>): T { return this.bar.get(clazz) as T; } public static setIt<T extends A>(clazz: Class<T>): T { const instance = new clazz(); this.bar.set(clazz, instance); return instance; } } // 使用示例:传入B类构造函数,返回B类型实例 const bInstance = Foo.setIt(B); // 类型为B const retrievedB = Foo.getIt(B); // 类型为B
如果需要让 Map 的键值类型完全精确对应(每个类构造函数对应其专属实例类型),可以用泛型对象替代 Map,但 Map 本身是同构容器,上述写法已经贴合你的核心需求。
2. 实现类似Kotlin的协变(in)/逆变(out)
TypeScript 通过泛型参数的协变、逆变规则实现类似 Kotlin in/out 的效果,核心依据是类型的使用场景:
协变(对应Kotlin的out)
协变允许子类类型向上兼容父类类型,适用于输出场景(比如返回值、只读属性)。TypeScript 中泛型默认在输出位置是协变的,也可以通过 readonly 或显式声明 out 强化约束:
// 协变容器:仅允许读取T类型值(输出) interface ReadOnlyContainer<out T> { get(): T; } // 合法:B是A的子类,ReadOnlyContainer<B>可以赋值给ReadOnlyContainer<A> const bContainer: ReadOnlyContainer<B> = { get: () => new B() }; const aContainer: ReadOnlyContainer<A> = bContainer;
逆变(对应Kotlin的in)
逆变允许父类类型向下兼容子类类型,适用于输入场景(比如函数参数、写入操作)。在泛型参数前添加 in 关键字即可声明逆变:
// 逆变容器:仅允许写入T类型值(输入) interface WriteOnlyContainer<in T> { set(value: T): void; } // 合法:A是B的父类,WriteOnlyContainer<A>可以赋值给WriteOnlyContainer<B> const aContainer: WriteOnlyContainer<A> = { set: (a: A) => {} }; const bContainer: WriteOnlyContainer<B> = aContainer;
对应你提供的 Kotlin 代码,TypeScript 实现如下:
type Class<T> = new (...args: any[]) => T; class Foo { public map: Map<Class<A>, A> = new Map(); // 泛型T约束为A的子类,实现逆变效果:接受A或其子类的构造函数 public setIt<T extends A>(clazz: Class<T>): A { const instance = new clazz(); this.map.set(clazz, instance); return instance; } }
如果需要更严格的逆变约束,可以通过泛型接口声明:
interface Foo<in T> { map: Map<Class<T>, T>; setIt(clazz: Class<T>): T; } // 合法:Foo<A>可以赋值给Foo<B>,符合逆变规则 const fooA: Foo<A> = { map: new Map(), setIt: (clazz) => new clazz() }; const fooB: Foo<B> = fooA;
内容的提问来源于stack exchange,提问作者TheSmileCat
相关产品推荐
相关产品推荐

