如何从单个泛型类型合并任意数量的TypeScript类型?
实现类型安全的对象更新函数
要实现你需要的类型安全update函数,核心是让operation的path和value类型严格绑定——不同路径对应正确的值类型。你已经实现了路径枚举PathString,接下来只需要补充路径到值类型的映射,再生成所有合法操作的联合类型即可。
步骤1:实现路径到值类型的映射PathValue
首先需要一个类型,用来根据给定的路径字符串,提取对象中对应位置的值类型:
type PathValue<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? PathValue<T[K], Rest> : never : P extends `${infer K}[${number}]` ? K extends keyof T ? T[K] extends (infer Item)[] ? Item : never : never : P extends keyof T ? T[P] : never;
这个类型会递归解析路径:
- 遇到
.分割的嵌套路径,逐层深入对象 - 遇到
[${number}]数组索引,提取数组的元素类型 - 直接匹配对象键,返回对应值类型
步骤2:生成所有合法操作的联合类型
基于你的PathString和上面的PathValue,可以生成所有合法的operation类型:
type Operation<T> = { [P in PathString<T>]: { path: P; value: PathValue<T, P>; }; }[PathString<T>];
这个类型会遍历所有合法路径PathString<T>,为每个路径生成对应的{path, value}类型,最后通过索引访问把所有类型合并成联合类型。
步骤3:实现update函数
现在可以写出类型安全的update函数了:
function update<T extends Record<string, any>>(obj: T, operation: Operation<T>): void { // 这里实现具体的更新逻辑,比如解析path并赋值 const keys = operation.path.split(/\.|\[|\]/).filter(Boolean); let current: any = obj; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; current = current[key]; } const lastKey = keys[keys.length - 1]; current[lastKey] = operation.value; }
测试验证
用你提供的示例对象测试:
const obj = { foo: "some string", bars: [{ text: "some text"}] }; // 合法调用 update(obj, { path: "foo", value: "new string" }); update(obj, { path: "bars", value: [{ text: "new item" }] }); update(obj, { path: "bars[0]", value: { text: "updated item" } }); update(obj, { path: "bars[0].text", value: "updated text" }); // 类型错误(value类型不匹配),TS会报错 update(obj, { path: "foo", value: 123 }); update(obj, { path: "bars[0].text", value: { text: "wrong type" } });
这样就完全实现了你需要的类型约束,确保每个operation的path和value类型严格对应。
内容的提问来源于stack exchange,提问作者QuantumZero
相关产品推荐
相关产品推荐

