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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:35:34