【问题标题】:Typescript - expression of type 'string' can't be used to index type打字稿-“字符串”类型的表达式不能用于索引类型
【发布时间】:2021-06-14 03:16:29
【问题描述】:
// common.js

const boxNames = ['one', 'two'];

module.exports {
  boxNames,
}
const common = require("./common.js");
const boxNames = common.boxNames;
const messages = {
  'one': {
      id: 'some id',
      text: 'some text',
  },
  'two': {
    id: 'some id',
    text: 'some text',
  },
};
const disabled = boxNames.map((key: string) => messages[key]);

以上代码为messages[key] 语句产生了TypeScript 错误:

TS7053:元素隐式具有“任何”类型,因为表达式 type 'string' 不能用于索引 type '{ one: { id: string;文本: 细绳; };二:{id:字符串;文本:字符串; }; }'。无索引 在“{ one:”类型上找到带有“字符串”类型参数的签名 { id:字符串;文本:字符串; };二:{id:字符串;文本:字符串; }; }'。

我将key 声明为字符串,而messages 的键是字符串,那么有什么问题呢?

(这里值得注意的是,messages 是使用 react-intl 库中的 defineMessages 方法生成的。)

(我在 StackOverflow 上看到了一些与 Object.keys 类似的答案,但我不确定这与我的 TypeScript 错误有何关系。)

【问题讨论】:

    标签: typescript


    【解决方案1】:

    对象messages 的键不是string 类型,而是"one" | "two" 类型。换句话说,它们是字符串文字,它是比简单的 string 更窄的类型,并且不能被更广泛的 string 类型索引。

    编译示例的一种简单方法是将键数组转换为元组:

    const boxNames = ['one', 'two'] as const;
    

    这有效地将类型从string[] 更改为["one", "two"],现在当您映射它们时,它们将具有正确的类型来索引您的对象。附带说明一下,没有必要显式设置 key 的类型,因为这可以从使用情况中推断出来。

    const disabled = boxNames.map((key) => messages[key]); // this is now fine
    

    另一种输入方式是将messages 的键分配给数组:

    const messages = {
      'one': {
          id: 'some id',
          text: 'some text',
      },
      'two': {
        id: 'some id',
        text: 'some text',
      },
    };
    
    const boxNames: (keyof typeof messages)[] = ['one', 'two'];
    
    const disabled = boxNames.map((key) => messages[key]);
    

    这比使用元组更具动态性,并且它将保持类型安全,因为您不允许在 boxNames 中包含比作为 messages 的键更多的字符串。我想象这仍然可以在您描述的require 场景中工作,尽管我现在没有设置环境来测试它。

    我认为这里的主要内容是boxNames 需要获取更具体的类型,或者messages 需要不那么具体的类型。我假设这些键 必须 完全匹配,但也许你的情况实际上更适合通过分配 messages 一个 string 索引类型来代替 - 我认为这里没有足够的上下文来假设无论哪种方式。这意味着要处理代码中您实际上不知道数组和对象在运行时是否匹配的可能性。

    【讨论】:

    • 很好的解释。考虑到 1)JavaScript 不支持它们(TypeScript 不应该仅限于输入本地语言中可用的内容),并且 2)这不利于 TypeScript 的逐步采用,因此宁愿不必将数组键入为元组如果我现在必须将包含 boxNames 的模块转换为 TypeScript 文件(在我的情况下,boxNames 是从 CommonJS 格式的文件中导入的值,因为它也需要在 NodeJs 中使用)(?)
    • 查看编辑。我相信这应该会有所帮助。
    • 我很想使用您提供的 const boxNames: (keyof typeof messages)[] = ['one', 'two']; 示例,但是,在我的情况下,因为消息和 boxNames 都被导入并且 boxNames 在不同的存储库中,这可能不起作用(?)我猜想在这种情况下我需要在 boxNames repo(?) 中定义一个类型定义文件
    【解决方案2】:

    Typescript 不知道 boxNames 的元素是 messages 的键。

    stringkeyof 之间有明显的区别。

    有几种方法可以告诉 TS 字符串是对象的键。

    无需自己测试,您也许可以这样做:

    boxNames.map((key: keyof typeof messages) => messages[key])
    

    你要查找keyof关键字的使用方法,必须在严格模式下索引对象。

    【讨论】:

      【解决方案3】:

      当你创建一个像下面这样的对象而不给出类型时,对象的推断类型将是Record<'one' | 'two', string>

      const numberCounter = {
       one: 'one',
       two: 'two'
      }
      

      因此,我们必须记住,对象的键始终是文字类型的联合,而对象的值将是值类型的联合。

      这里在下面的例子中,键是'one'和'two'的并集,值只是字符串类型(字符串和字符串的并集是字符串。)

      我曾经像下面这样键入对象,所以我从来没有遇到过这样的问题。这里我明确地说我的对象键是字符串类型的。

      const boxNames = ['one', 'two'] ;
      
      interface Type {
        id: string;
        text: string;
      }
      const messages: Record<string, Type> = {
        one: {
            id: 'some id',
            text: 'some text',
        },
        two: {
          id: 'some id',
          text: 'some text',
        },
      };
      // key is already inferred as string
      const disabled = boxNames.map(key => messages[key]);
      console.log(disabled);
      
      

      如果您不想明确地说对象键是字符串,那么@lawrence-witt answer 将是最合适的选择。

      还有另一种方法可以使键成为“一”和“二”的并集。

      const boxNames = ['one', 'two'];
      
      const messages = {
        one: {
            id: 'some id',
            text: 'some text',
        },
        two: {
          id: 'some id',
          text: 'some text',
        },
      };
      
      type Key = keyof typeof messages;
      
      // here we are doing type cast of string into union of `one` and `two`.
      //boxNames key are of string type
      const disabled = boxNames.map(key => messages[key as Key]);
      console.log(disabled);
      

      【讨论】:

      • 这是否表明defineMessages的返回类型定义存在问题?
      • 不能多说这个?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-23
      • 2021-03-05
      • 2019-12-12
      • 2021-08-31
      • 2021-03-17
      • 1970-01-01
      • 2021-10-25
      相关资源
      最近更新 更多