打字稿 - “字符串”类型的表达式不能用于索引类型

问题描述

// 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',};
const disabled = BoxNames.map((key: string) => messages[key]);

上面的代码messages[key] 语句产生了 TypeScript 错误

TS7053:元素隐式具有“任何”类型,因为表达式为 类型 'string' 不能用于索引类型 '{ one: { id: string;文本: 细绳; };二:{id:字符串;文本:字符串; }; }'。无索引 在类型 '{ 一: { ID:字符串;文本:字符串; };二:{id:字符串;文本:字符串; }; }'。

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

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

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

解决方法

对象 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',};

const boxNames: (keyof typeof messages)[] = ['one','two'];

const disabled = boxNames.map((key) => messages[key]);

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

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

,

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

stringkeyof 之间有明显的区别。

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

无需亲自测试,您也许可以做到这一点:

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

要查询如何使用keyof关键字,需要在严格模式下索引对象。

,

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

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

所以,我们在这里必须记住,对象的键总是文字类型的联合,对象的值将是值类型的联合。

在下面的例子中,键是'一'和'二'的并集,值只是字符串类型(字符串和字符串的并集是一个字符串。)

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

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

interface Type {
  id: string;
  text: string;
}
const messages: Record<string,Type> = {
  one: {
      id: 'some id',two: {
    id: 'some id',};
// 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',};

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);

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...