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

TypeScript运行时为类添加动态字段并保证类型安全的实现方法

解决方案

可以通过TS泛型+映射类型+声明合并的组合实现需求,核心思路是收紧模块name属性的类型范围,再从传入的模块联合类型中自动生成容器的动态属性类型,具体实现如下:

核心改动点

  • 改造Module基类,将name属性约束为字面量类型,确保TS可以提取每个模块的固定名称作为类型键
  • 定义映射工具类型,将模块联合类型转换为「模块名-模块实例」的键值对类型
  • 利用TS同名interface和class的声明合并特性,让容器类自动继承动态属性的类型定义
  • 调整容器init方法的入参类型,保证传入模块和泛型约束一致

完整实现代码

// 改造基类,约束name为字面量类型
abstract class Module<TName extends string> {
  public abstract readonly name: TName;
}

class ThisModule extends Module<'thisModule'> {
  public readonly name = 'thisModule' as const;

  public log = () => {
    console.log('ThisModule called!');
  };
}

class ThatModule extends Module<'thatModule'> {
  public readonly name = 'thatModule' as const;

  public log = () => {
    console.log('ThatModule called!');
  };
}

// 映射工具类型:从模块联合类型生成键值对类型
type ModuleMap<T extends Module<string>> = {
  [K in T['name']]: Extract<T, { name: K }>
}

// 声明合并:容器实例同时包含原生方法和动态模块属性
interface Container<TModule extends Module<string>> extends ModuleMap<TModule> {}
class Container<TModule extends Module<string>> {
  public init = (modules: TModule[]) => {
    for (const module of modules) {
      // 类型断言合法,运行时确实会挂载对应属性
      (this as unknown as ModuleMap<TModule>)[module.name] = module;
    }
  };
}

// 测试代码
const thisModule = new ThisModule();
const thatModule = new ThatModule();

// 传入模块联合类型作为泛型参数
const container = new Container<ThisModule | ThatModule>();

container.init([thisModule, thatModule]);

// 无类型错误,可正确推导log方法类型
container.thisModule.log();
container.thatModule.log();

// 触发类型错误:Property 'anythingElse' does not exist on type 'Container<ThisModule | ThatModule>'
container.anythingElse.log();

扩展说明

如果希望进一步简化调用,不需要显式传入泛型参数,可以额外封装工厂函数,让TS自动从init的入参推导模块类型,不需要手动写联合类型。

内容的提问来源于stack exchange,提问作者nerdlinger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 17:45:00