'string' 不能用于索引类型 '{}'

Max*_*hke 62 javascript typescript reactjs

我有以下 React 组件,可从对象数组生成 HTML 表。应该显示的列是通过tableColumns属性定义的。

当循环items并显示正确的列时,我必须使用对象 ( ) 中的key属性,但打字稿生成以下错误:tableColumn{item[column.key]}

元素隐式具有 'any' 类型,因为类型 'string' 的表达式不能用于索引类型 '{}'。在类型“{}”上找不到带有“字符串”类型参数的索引签名。

我能做些什么来解决这个问题?我迷路了

我如何调用组件:

<TableGridView
  items={[
    {
      id: 1,
      name: 'John Doe',
      email: 'john@doe.de'
    },
    {
      id: 2,
      name: 'Lorem ipsum',
      email: 'lorem@ipsum.com',
    }
  ]}
  tableColumns={[
    {
      key: 'id',
      label: 'ID',
    },
    {
      key: 'name',
      label: 'Name',
    }
  ]}
/>
Run Code Online (Sandbox Code Playgroud)

我的组件:

export type TableColumn = {
  key: string,
  label: string,
};

export type TableGridViewProps = {
  items: object[],
  tableColumns: TableColumn[]
};

const TableGridView: React.FC<TableGridViewProps> = ({ tableColumns, items }) => {
  return (
    <table>
      <tbody>
        {items.map(item => {
          return (
            <tr>
              {tableColumns.map((column, index) => {
                return (
                  <td
                    key={column.key}
                    className="lorem ipsum"
                  >
                    {item[column.key]} // error thrown here
                  </td>
                );
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*lms 58

  items: object[],
Run Code Online (Sandbox Code Playgroud)

虽然从技术上讲它是一个 JavaScript 对象,但类型可以更好。为了让 Typescript 在访问对象属性时正确帮助您识别错误,您需要告诉它对象的确切形状。如果您将其键入为object,则打字稿无法帮助您。相反,您可以告诉它对象具有的确切属性和数据类型:

  let assistance: { safe: string } = { safe: 1 /* typescript can now tell this is wrong */ };
  assistance.unknown; // typescript can tell this wont really work too
Run Code Online (Sandbox Code Playgroud)

现在,如果对象可以包含任何类型的键/值对,您至少可以通过使用对象索引类型告诉打字稿值(和键)具有什么类型:

 items: {
   [key: string]: number | string,
  }[]
Run Code Online (Sandbox Code Playgroud)

在给定的情况下,这将是准确的类型。

  • 抱歉,您的回答并没有解释问题,只是表明情况很糟糕。请考虑添加解释。人们期望能够在对象上使用字符串索引,而不必显式定义它。 (14认同)
  • @papiro 不,还有符号(和数字,从打字稿的角度来看) (3认同)
  • @maksion *有人会期望* ...我不会?Typescript 如何知道 object[string] 是什么类型?它是*any*,因此它是一个不好的类型。 (2认同)

Ale*_*kay 9

使用泛型

// bad
const _getKeyValue = (key: string) => (obj: object) => obj[key];

// better
const _getKeyValue_ = (key: string) => (obj: Record<string, any>) => obj[key];

// best
const getKeyValue = <T extends object, U extends keyof T>(key: U) => (obj: T) =>
      obj[key];
Run Code Online (Sandbox Code Playgroud)

不好 - 错误的原因是object默认情况下类型只是一个空对象。因此不可能使用string类型来索引{}

更好 - 错误消失的原因是因为现在我们告诉编译器obj参数将是字符串/值 ( string/any) 对的集合。但是,我们正在使用该any类型,因此我们可以做得更好。

最佳 -T扩展空对象。U扩展T. 因此U将始终存在于T,因此它可以用作查找值。

这是一个完整的例子:

我已经切换了泛型的顺序(U extends keyof T现在在之前T extends object)以强调泛型的顺序并不重要,您应该选择一个对您的函数最有意义的顺序。

const getKeyValue = <U extends keyof T, T extends object>(key: U) => (obj: T) =>
  obj[key];

interface User {
  name: string;
  age: number;
}

const user: User = {
  name: "John Smith",
  age: 20
};

const getUserName = getKeyValue<keyof User, User>("name")(user);

// => 'John Smith'
Run Code Online (Sandbox Code Playgroud)


ark*_*k0n 9

如果它是一个迂腐的 javascript 对象,对每个字段创建类型定义没有意义,并且作为类定义也没有意义,那么您可以键入对象,any并且 typescript 会让您随意索引。

前任。

//obviously no one with two brain cells is going to type each field individually
let complicatedObject: any = {
    attr1: 0,
    attr2: 0,
    ...
    attr999: 0
}

Object.keys(complicatedObject).forEach(key => {
    complicatedObject[key] += 1;
}
Run Code Online (Sandbox Code Playgroud)

  • 赞成,因为 Typescript 通常很有用,但有时会像 OP 的问题一样妨碍。您可以花一整天的时间尝试输入所有最后的内容(而不提供任何内容),或者采取合理的中间道路“在有帮助、有意义时使用它”。最后请记住,在运行时它是 JavaScript,无论您的类型系统有多可爱,并且您的类型定义可能实际上并不能反映代码中运行的内容的实际情况(尤其是当涉及 API 调用时!) (12认同)
  • 真是一团糟。我使用 Typescript 已经有 3 年了,但它并没有让我的生活变得更轻松。解决方法总是在解决方法之上。在我看来,类型检查 javascript 带来的问题比它解决的问题还要多。 (4认同)
  • 投反对票是因为 TypeScript 解决方法非常丑陋。 (3认同)
  • 如果您键入按键,这实际上不起作用。 (3认同)

Cha*_*ong 5

首先,将类型定义items为数组objects是一个坏主意,因为它违背了 Typescript 的目的。而是定义数组将包含什么类型的对象。我会这样做:

type Item = {
  id: number,
  name: string,
  email: string,
}

export type TableGridViewProps = {
  items: Item[],
  tableColumns: TableColumn[]
};
Run Code Online (Sandbox Code Playgroud)

之后,如果您绝对确定key将存在于 中item,则可以执行以下操作来告诉 Typescript 您正在访问有效索引。

<td
  key={column.key}
  className="lorem ipsum"
>
  {item[column.key as keyof typeof Item]}
</td>
Run Code Online (Sandbox Code Playgroud)

  • @Kraken也许我的措辞错误,但我想说的是,如果您已经知道对象的形状,那么这是一个很好的做法,您不应该只是为了快速解决方法而将其设为“对象”。这样做你会牺牲 Typescript 带来的很多好处 (3认同)