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

使用ES5与JSDoc构建类型安全类系统时,如何为构造函数实例类型添加属性并保留原类型参数?

使用ES5与JSDoc构建类型安全类系统时,如何为构造函数实例类型添加属性并保留原类型参数?

我最近在折腾用ES5搭配JSDoc做一个类型安全的类系统,核心需求其实很直白:给构造函数返回的实例类型额外加个属性,同时还得完完整整保留原构造函数的类型参数。

先给你看看我写的cls包装函数,初衷就是给构造函数的实例添属性:

/**
 * @param {C} constructor
 * @template {new (...args: any) => {}} C
 * @returns {new (...args: ConstructorParameters<C>) => InstanceType<C> & { addedProp: "addedProp" }}
 */
function cls(constructor) {
  // 实现细节先放一边,重点看类型定义部分
  return;
}

用法大概是这样的,比如我定义一个带泛型的构造函数,用cls包一下:

var A = cls(
  /**
   * @constructor
   * @param {T} a
   * @template T
   */
  function A(a) {
    this.a = a;
  }
);

遇到的问题

结果踩了个坑:当构造函数本身带有泛型参数的时候,经过cls包装后,原有的泛型参数好像没被正确保留。就拿这个原构造函数来说:

/**
 * @constructor
 * @param {T} a
 * @template T
 */
function A(a) {
  this.a = a;
}

它本身是带泛型T的——也就是创建实例时传入什么类型的a,实例的a属性就是对应类型。但用原来的cls包装后,返回的A构造函数好像丢失了这个泛型能力,实例的a属性类型没法正确推断了。

解决思路

后来我发现问题出在cls的泛型约束上。原来的@template {new (...args: any) => {}} C只约束了C是一个非泛型的构造函数,完全没考虑构造函数本身带泛型参数的情况。

那怎么调整呢?只需要修改cls的JSDoc泛型定义,让它能接受本身带有泛型参数的构造函数就行。比如把泛型约束改成支持带泛型的构造函数:

/**
 * @param {C} constructor
 * @template {new <T>(...args: any) => {}} C
 * @returns {new <T>(...args: ConstructorParameters<C>) => InstanceType<C> & { addedProp: "addedProp" }}
 */
function cls(constructor) {
  // 实现细节忽略
  return;
}

如果原构造函数有多个泛型参数,还可以写成<T, U>这种形式,灵活适配不同的构造函数。

验证效果

现在再用修改后的cls来定义A:

var A = cls(
  /**
   * @constructor
   * @param {T} a
   * @template T
   */
  function A(a) {
    this.a = a;
  }
);

// 这时候new A(123)的实例类型会被正确推断为:{ a: number, addedProp: "addedProp" }
// new A('hello')的实例类型就是:{ a: string, addedProp: "addedProp" }

完美!原构造函数的泛型参数T被完整保留了,实例同时拥有原属性a和新增的addedProp,类型检查完全正常。

核心原因就是调整后的泛型约束能正确“识别”带泛型的构造函数,InstanceType<C>和ConstructorParameters<C>也能正确处理这类构造函数,最终返回的新构造函数自然就继承了原有的泛型能力,实例类型也实现了原类型与新增属性的组合。

备注:内容来源于stack exchange,提问作者Shmookoff

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 15:34:32