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), }, });
关键细节解释
ConfigItem<T, D>:- 泛型
T绑定整个配置对象,确保依赖D只能是T的键,避免非法依赖 - 使用
Awaited<ReturnType<T[K]['action']>>提取依赖action返回的Promise最终值类型,而非Promise本身
- 泛型
ConfigObject<T>:- 自引用约束
T extends Record<string, ConfigItem<T>>,强制每个配置项的依赖只能指向当前对象的属性,保证类型合法性
- 自引用约束
createObject函数:- 泛型约束让TypeScript自动遍历整个对象结构,完成所有配置项的类型推断,无需手动指定泛型参数
效果验证
- 若
dependencies填写不存在的属性名,TypeScript会直接报错 action的参数会自动获得正确的类型,比如示例中baz的foo参数类型为readonly [1, 2, 3],bar为string- 无依赖的配置项(如
qux)的action无需参数,类型检查正常通过
内容的提问来源于stack exchange,提问作者Manuel Ibar
相关产品推荐
相关产品推荐

