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

TypeScript中如何校验字符串属性路径对指定类型是否合法?

TypeScript 对象属性路径校验实现

下面分别提供*编译时类型安全(支持IDE智能补全、实时报错)*和纯运行时校验两种方案,完全匹配你需要的点属性访问、数组下标访问、字符串键名方括号访问的路径格式。


编译时类型安全方案

这套方案基于TypeScript类型系统实现,不需要额外依赖,IDE会在你输入路径时逐层给出属性提示,非法路径会在编写代码时直接标红,同时会自动校验传入值的类型是否和路径目标属性匹配。

// 解析路径字符串为逐段访问的元组
type ParsePath<S extends string> =
  S extends `${infer Head}.${infer Tail}`
    ? [...ParsePath<Head>, ...ParsePath<Tail>]
    : S extends `${infer Key}[${infer Index}]`
      ? ParsePath<Key> extends [...infer ParentPath]
        ? Index extends `'${infer RealKey}'` | `"${infer RealKey}"`
          ? [...ParentPath, RealKey]
          : [...ParentPath, number]
        : never
      : S extends "" ? [] : [S];

// 根据路径元组推导目标位置的值类型
type ValueAtPath<T, Path extends unknown[]> =
  Path extends [infer First, ...infer Rest]
    ? First extends keyof T
      ? ValueAtPath<T[First], Rest>
      : First extends number
        ? T extends Array<infer Item>
          ? ValueAtPath<Item, Rest>
          : never
        : never
    : T;

// 校验路径是否为目标类型的合法访问路径
type ValidPath<T, S extends string> =
  ParsePath<S> extends infer P
    ? P extends unknown[]
      ? ValueAtPath<T, P> extends never ? never : S
      : never
    : never;

function setValue<T, P extends string>(
  object: T,
  path: P & ValidPath<T, P>,
  value: ValueAtPath<T, ParsePath<P>>
) {
  const segments: (string | number)[] = [];
  // 匹配所有路径格式:.key、[num]、['key']、["key"]
  const pathRegex = /([^.[\]]+)|\[(\d+)\]|\['([^']+)'\]|\["([^"]+)"\]/g;
  let match: RegExpExecArray | null;
  while ((match = pathRegex.exec(path)) !== null) {
    if (match[1]) segments.push(match[1]);
    else if (match[2]) segments.push(Number(match[2]));
    else segments.push(match[3] || match[4]);
  }

  // 逐层遍历赋值,中间节点不存在时自动创建
  let current: any = object;
  for (let i = 0; i < segments.length - 1; i++) {
    const seg = segments[i];
    if (current[seg] == null) {
      current[seg] = typeof segments[i + 1] === 'number' ? [] : {};
    }
    current = current[seg];
  }
  current[segments.at(-1)!] = value;
}

效果验证

测试基于你定义的TestObject类型:

type TestObject = {
  arr: Array<{x: number, y: Array<{z: string}>}>;
  obj: {[key: string]: number};
}

const o: TestObject = { arr: [], obj: {} };
setValue<TestObject>(o, "arr[0].x", 1); // 校验通过
setValue<TestObject>(o, "arr[0].y[2].z", "hello world"); // 校验通过
setValue<TestObject>(o, "obj['x']", 1); // 校验通过

非法路径会直接触发编译错误:

setValue<TestObject>(o, "a", 1); // 类型报错,"a"不属于TestObject的合法访问路径

注:由于编译阶段无法获知数组实际长度,所有合法数字下标都会通过校验,符合数组动态长度的实际使用场景。


纯运行时校验方案

如果不需要编译时检查,只需要在代码运行时拦截非法路径,可以使用以下JS实现,无类型依赖:

function setValue(object, path, value) {
  const segments = [];
  const pathRegex = /([^.[\]]+)|\[(\d+)\]|\['([^']+)'\]|\["([^"]+)"\]/g;
  let match;
  while ((match = pathRegex.exec(path)) !== null) {
    if (match[1]) segments.push(match[1]);
    else if (match[2]) segments.push(Number(match[2]));
    else segments.push(match[3] || match[4]);
  }

  let current = object;
  for (let i = 0; i < segments.length - 1; i++) {
    const seg = segments[i];
    if (current == null || !(seg in current)) {
      throw new Error(`非法路径:属性${String(seg)}不存在`);
    }
    current = current[seg];
  }
  const lastSeg = segments.at(-1);
  if (current == null) {
    throw new Error(`非法路径:目标节点为空`);
  }
  current[lastSeg] = value;
}

该实现会在运行时逐段校验路径是否存在,遇到非法访问直接抛出错误,如果需要和编译时版本对齐支持自动创建中间节点,把对应校验逻辑替换为自动创建数组/对象的逻辑即可。


内容的提问来源于stack exchange,提问作者Scott Schafer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:57:14