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

非递归遍历TypeScript代码出现Element implicitly has an 'any' type类型错误求助

问题描述

我正尝试将一个非递归遍历对象的JavaScript函数改写为TypeScript,访问entry[key]时触发如下TypeScript错误:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'StringObjectOrArray'.
No index signature with a parameter of type 'string' was found on type 'StringObjectOrArray'.ts(7053)

我对此感到困惑:for..in遍历数组时会返回字符串形式的索引,而字符串形式的整数本可直接作为数组索引使用,我到底忽略了什么?

我的TypeScript代码:

type StringKeyObject = { [key: string]: unknown };
type StringObjectOrArray = StringKeyObject | StringKeyObject[];

function traverse(obj: StringObjectOrArray) {
  if (!obj) {
    return;
  }

  const stack: [string, StringObjectOrArray][] = [['root', obj]];
  while (stack.length) {
    const [path, entry] = stack[0];
    for (const key in entry) {
      if (entry[key] && typeof entry[key] === "object") { // ts error here
        stack.push([`${path}.${key}`, entry[key]]);
      } else {
        console.log("%s: %s", `${path}.${key}`, entry[key]);
      }
    }
    stack.shift();
  }
}

可正常运行的JavaScript代码如下:

function traverse(obj) {
  if (!obj) {
    return;
  }

  const stack = [['root', obj]];
  while (stack.length) {
    const [path, entry] = stack[0];
    for (const key in entry) {
      if (entry[key] && typeof entry[key] === "object") {
        stack.push([`${path}.${key}`, entry[key]]);
      } else {
        console.log("%s: %s", `${path}.${key}`, entry[key]);
      }
    }
    stack.shift();
  }
}

traverse({
    one: "hello",
    two: "world",
    three: {
        one: 1,
        two: 2,
        three: 4,
        four: [{
            one: true,
            two: false
        }]
    },
    four: "!"
});
问题原因与解决方案

原因分析

TypeScript的类型检查严格区分了对象和数组的索引类型:

  • 你定义的StringKeyObject使用字符串索引,但数组的索引类型在TS里是number(虽然JS里用字符串数字能访问数组,但TS类型系统不认可字符串作为数组的合法索引类型)。
  • StringObjectOrArray是对象和数组的联合类型,当你用string类型的key去索引这个联合类型时,TS找不到同时支持字符串索引的类型定义,因此抛出错误。

解决方案

推荐通过类型收窄的方式处理,这更符合TS的类型安全规范,修正后的代码如下:

type StringKeyObject = { [key: string]: unknown };
type StringObjectOrArray = StringKeyObject | StringKeyObject[];

function traverse(obj: StringObjectOrArray) {
  if (!obj) {
    return;
  }

  const stack: [string, StringObjectOrArray][] = [['root', obj]];
  while (stack.length) {
    const [path, entry] = stack.shift()!;
    if (Array.isArray(entry)) {
      // 处理数组:把字符串索引转为number类型访问
      for (const key in entry) {
        const value = entry[Number(key)];
        if (value && typeof value === "object") {
          stack.push([`${path}.${key}`, value as StringObjectOrArray]);
        } else {
          console.log("%s: %s", `${path}.${key}`, value);
        }
      }
    } else {
      // 处理对象:直接用字符串索引访问
      for (const key in entry) {
        const value = entry[key];
        if (value && typeof value === "object") {
          stack.push([`${path}.${key}`, value as StringObjectOrArray]);
        } else {
          console.log("%s: %s", `${path}.${key}`, value);
        }
      }
    }
  }
}

如果想简化代码,也可以给数组类型添加字符串索引签名,让TS认可字符串索引:

type StringKeyObject = { [key: string]: unknown };
// 给数组扩展字符串索引签名
type StringObjectOrArray = StringKeyObject | (StringKeyObject[] & { [key: string]: unknown });

function traverse(obj: StringObjectOrArray) {
  if (!obj) {
    return;
  }

  const stack: [string, StringObjectOrArray][] = [['root', obj]];
  while (stack.length) {
    const [path, entry] = stack[0];
    for (const key in entry) {
      const value = entry[key];
      if (value && typeof value === "object") {
        stack.push([`${path}.${key}`, value as StringObjectOrArray]);
      } else {
        console.log("%s: %s", `${path}.${key}`, value);
      }
    }
    stack.shift();
  }
}

补充说明

JS中字符串形式的整数确实能访问数组,但TS为了保证类型安全,强制要求数组使用number类型索引。通过类型收窄或者扩展索引签名,就能让TS正确识别索引的合法性,消除错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 12:17:47