TypeScript自定义Store函数的Payload类型安全标注问题问询
解决方案:为自定义Store的set方法添加强类型约束
要解决这个问题,我们可以利用TypeScript的泛型和递归类型工具,为Store的set方法建立键路径与对应值类型的强绑定,同时支持任意结构的存储数据类型,完全避免使用any。
1. 定义核心类型工具
首先需要两个关键类型:生成合法路径的Path,以及根据路径获取对应值类型的PathValue:
// 递归生成所有合法的点分隔路径(支持嵌套对象) type Path<T> = T extends object ? { [K in keyof T]: K extends string | number ? `${K}` | `${K}:${Path<T[K]>}` : never; }[keyof T] : never; // 根据路径递归解析对应的值类型 type PathValue<T, P extends Path<T>> = P extends `${infer K}:${infer Rest}` ? K extends keyof T ? Rest extends Path<T[K]> ? PathValue<T[K], Rest> : never : never : P extends keyof T ? T[P] : never;
2. 实现带类型约束的Store函数
基于上面的类型工具,实现createStore函数,它接收任意类型的初始数据,并返回带有强类型约束的get/set方法:
function createStore<T>(initialData: T) { const data = { ...initialData }; return { // 根据路径获取对应值,返回类型自动匹配 get<P extends Path<T>>(path: P): PathValue<T, P> { const segments = path.split(':') as Array<keyof any>; let current: any = data; for (const seg of segments) { current = current[seg]; } return current as PathValue<T, P>; }, // 路径与值类型强绑定,传入错误类型直接触发TS报错 set<P extends Path<T>>(path: P, value: PathValue<T, P>): void { const segments = path.split(':') as Array<keyof any>; let current: any = data; // 遍历到倒数第二个路径段 for (let i = 0; i < segments.length - 1; i++) { const seg = segments[i]; current = current[seg]; } // 给最后一个路径段赋值 const lastSeg = segments[segments.length - 1]; current[lastSeg] = value; } }; }
3. 测试示例
用你提到的StoredData类型测试,验证类型约束是否生效:
// 示例存储数据类型 type StoredData = { num: number; str: string; obj: { a: number; b: string; nested: { val: boolean; }; }; }; const initialData: StoredData = { num: 10, str: 'hello', obj: { a: 100, b: 'world', nested: { val: true } } }; const store = createStore(initialData); // ✅ 合法调用:类型完全匹配,无报错 store.set('num', 20); store.set('obj:a', 200); store.set('obj:nested:val', false); // ❌ 非法调用:TS直接报错,类型不匹配 store.set('obj:a', 'a'); // 类型“string”的参数不能赋给类型“number”的参数 store.set('str', 123); // 类型“number”的参数不能赋给类型“string”的参数
关键说明
Path<T>会递归遍历对象的所有层级属性,生成所有合法的点分隔路径字符串,比如obj:a、obj:nested:val等。PathValue<T, P>会根据传入的路径P,自动解析出对应的值类型,确保set方法的value参数与路径指向的属性类型严格一致。- 泛型
T让createStore支持任意结构的存储数据,不需要提前固定为某一种类型。 - 路径解析的逻辑是基础实现,你可以根据需求扩展(比如处理数组索引、不存在的路径容错等),类型约束部分已经完成核心功能。
内容的提问来源于stack exchange,提问作者user3331344
相关产品推荐
相关产品推荐

