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

TypeScript构建对象时的类型安全实现方案求助

TypeScript 对象构建的类型安全实现方案

需求说明

需要实现对象构建时的类型安全,核心要求:

  • 每个配置项的dependencies数组只能包含根对象已存在的属性名
  • 当配置项指定dependencies时,action函数的参数类型会自动推断:参数是包含依赖属性的对象,属性值对应依赖action返回的Promise解析后类型
  • dependencies为可选属性,无依赖时action无需接收参数

问题分析

原实现中因为ConfigObject的泛型存在循环引用,导致TypeScript无法正确推断action的参数类型,最终参数被识别为any,无法实现类型安全。

解决方案

通过调整类型结构,利用TypeScript的自引用泛型约束和Awaited工具类型,实现完整的类型推断和安全校验:

// 单个配置项类型:T为整个配置对象,D为当前项依赖的键集合
type ConfigItem<T extends Record<string, any>, D extends keyof T = never> = {
  action: (deps: { [K in D]: Awaited<ReturnType<T[K]['action']>> }) => Promise<any>;
  dependencies?: readonly D[];
};

// 整个配置对象的类型约束,确保每个配置项的依赖都是自身的键
type ConfigObject<T extends Record<string, ConfigItem<T>>> = T;

// 创建配置对象的工厂函数,自动推断所有类型
function createObject<T extends Record<string, ConfigItem<T>>>(obj: T): T {
  return obj;
}

// 示例使用
const obj = createObject({
  foo: {
    action: () => Promise.resolve([1, 2, 3] as const),
  },
  bar: {
    action: () => Promise.resolve("hello"),
  },
  baz: {
    action: ({ foo, bar }) => {
      // foo 类型:readonly [1, 2, 3],bar 类型:string
      console.log(foo[0], bar);
      return Promise.resolve(true);
    },
    dependencies: ['foo', 'bar'],
  },
  qux: {
    action: () => Promise.resolve(42),
  },
});

关键细节解释

  1. ConfigItem<T, D>:
    • 泛型T绑定整个配置对象,确保依赖D只能是T的键,避免非法依赖
    • 使用Awaited<ReturnType<T[K]['action']>>提取依赖action返回的Promise最终值类型,而非Promise本身
  2. ConfigObject<T>:
    • 自引用约束T extends Record<string, ConfigItem<T>>,强制每个配置项的依赖只能指向当前对象的属性,保证类型合法性
  3. createObject函数:
    • 泛型约束让TypeScript自动遍历整个对象结构,完成所有配置项的类型推断,无需手动指定泛型参数

效果验证

  • 若dependencies填写不存在的属性名,TypeScript会直接报错
  • action的参数会自动获得正确的类型,比如示例中baz的foo参数类型为readonly [1, 2, 3],bar为string
  • 无依赖的配置项(如qux)的action无需参数,类型检查正常通过

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:32:35