“字符串”不能用于索引类型“{}”

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

当循环通过 items并显示正确的列时,我必须使用来自 tableColumn对象({item[column.key]})的 key属性,但是输入法会产生以下错误:

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

What could I do to fix this? I'm lost

我如何调用组件:

<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',
}
]}
/>

My Component:

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>
);
}
145881 次浏览
  items: object[],

虽然技术上它是一个 JavaScript 对象,但类型可以更好。为了正确地帮助您识别访问对象属性时的错误,您需要告诉它对象的确切形状。如果你输入它作为 object,打字稿不能帮助你。相反,您可以告诉它对象具有的确切属性和数据类型:

  let assistance: { safe: string } = { safe: 1 /* typescript can now tell this is wrong */ };
assistance.unknown; // typescript can tell this wont really work too

现在,在对象可以包含任何类型的键/值对的情况下,您至少可以通过使用对象索引类型来告诉类型脚本这些值(和键)具有什么类型:

 items: {
[key: string]: number | string,
}[]

这将是准确的类型,在案件给出。

使用非专利药

// 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];

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

Better - the reason the error disappears is because now we are telling the compiler the obj argument will be a collection of string/value (string/any) pairs. However, we are using the any type, so we can do better.

Best-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'

尝试添加任意类型[]

items:any[]

如果它是一个学究气的 javascript 对象,没有意义为每个字段创建类型定义,也没有意义作为一个类定义,你可以用 any键入对象,类型脚本将让你索引任何你想要的。

前男友。

//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;
}

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

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


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

在此之后,如果您完全确定 key将存在于 item中,那么可以执行以下操作来告诉 Type 脚本您正在访问一个有效的索引。

<td
key={column.key}
className="lorem ipsum"
>
{item[column.key as keyof typeof Item]}
</td>