You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:20:38