元素隐含有一个'any'Type 'string'不能用于索引

尝试一个React项目的TypeScript,我被这个错误困住了:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ train_1: boolean; train_2: boolean; train_3: boolean; train_4: boolean; }'.
No index signature with a parameter of type 'string' was found on type '{ train_1: boolean; train_2: boolean; train_3: boolean; train_4: boolean; }'

当我试图在我的组件中过滤数组时出现

.filter(({ name }) => plotOptions[name]);

到目前为止,我查看了文章“在TypeScript中索引对象”(https://dev.to/kingdaro/indexing-objects-in-typescript-1cgi),因为它有类似的错误,但我试图将索引签名添加到类型plotTypes,我仍然得到相同的错误。

我的组件代码:

import React, { Component } from "react";
import createPlotlyComponent from "react-plotly.js/factory";
import Plotly from "plotly.js-basic-dist";
const Plot = createPlotlyComponent(Plotly);


interface IProps {
data: any;
}


interface IState {
[key: string]: plotTypes;
plotOptions: plotTypes;
}


type plotTypes = {
[key: string]: boolean;
train_1: boolean;
train_2: boolean;
train_3: boolean;
train_4: boolean;
};


interface trainInfo {
name: string;
x: Array<number>;
y: Array<number>;
type: string;
mode: string;
}


class FiltrationPlots extends Component<IProps, IState> {
readonly state = {
plotOptions: {
train_1: true,
train_2: true,
train_3: true,
train_4: true
}
};
render() {
const { data } = this.props;
const { plotOptions } = this.state;


if (data.filtrationData) {
const plotData: Array<trainInfo> = [
{
name: "train_1",
x: data.filtrationData.map((i: any) => i["1-CumVol"]),
y: data.filtrationData.map((i: any) => i["1-PressureA"]),
type: "scatter",
mode: "lines"
},
{
name: "train_2",
x: data.filtrationData.map((i: any) => i["2-CumVol"]),
y: data.filtrationData.map((i: any) => i["2-PressureA"]),
type: "scatter",
mode: "lines"
},
{
name: "train_3",
x: data.filtrationData.map((i: any) => i["3-CumVol"]),
y: data.filtrationData.map((i: any) => i["3-PressureA"]),
type: "scatter",
mode: "lines"
},
{
name: "train_4",
x: data.filtrationData.map((i: any) => i["4-CumVol"]),
y: data.filtrationData.map((i: any) => i["4-PressureA"]),
type: "scatter",
mode: "lines"
}
].filter(({ name }) => plotOptions[name]);
return (
<Plot
data={plotData}
layout={{ width: 1000, height: 1000, title: "A Fancy Plot" }}
/>
);
} else {
return <h1>No Data Loaded</h1>;
}
}
}


export default FiltrationPlots;


605435 次浏览

发生这种情况是因为您试图使用字符串name访问plotOptions属性。TypeScript理解name可以有任何值,而不仅仅是plotOptions的属性名。所以TypeScript需要在plotOptions中添加索引签名,这样它就知道你可以在plotOptions中使用任何属性名。但我建议改变name的类型,因此它只能是plotOptions属性之一。

interface trainInfo {
name: keyof typeof plotOptions;
x: Array<number>;
y: Array<number>;
type: string;
mode: string;
}

现在你只能使用存在于plotOptions中的属性名。

您还必须稍微更改您的代码。

首先将数组赋值给某个临时变量,这样TS就知道数组类型了:

const plotDataTemp: Array<trainInfo> = [
{
name: "train_1",
x: data.filtrationData.map((i: any) => i["1-CumVol"]),
y: data.filtrationData.map((i: any) => i["1-PressureA"]),
type: "scatter",
mode: "lines"
},
// ...
}

然后过滤:

const plotData = plotDataTemp.filter(({ name }) => plotOptions[name]);

如果你从API获取数据,并且在编译时没有办法输入检查道具,唯一的方法是在plotOptions中添加索引签名:

type tplotOptions = {
[key: string]: boolean
}


const plotOptions: tplotOptions = {
train_1: true,
train_2: true,
train_3: true,
train_4: true
}

这就是它对我的作用。tsconfig.json有一个选项noImplicitAny,它被设置为true,我只是简单地将它设置为false,现在我可以使用字符串访问对象中的属性。

// 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类型来索引{}

更好-错误消失的原因是因为现在我们告诉编译器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'

替代语法

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

typescript错误

    const formData = new FormData();
Object.keys(newCategory).forEach((k,i)=>{
var d =Object.values(newCategory)[i];
formData.append(k,d)
})

当我们做类似这样的事情时,obj[key] Typescript不能确定该键是否存在于该对象中。我做了什么:

Object.entries(data).forEach(item => {
formData.append(item[0], item[1]);
});

多亏了Alex Mckay,我才有了动态设置道具的决心:

  for(let prop in filter)
(state.filter as Record<string, any>)[prop] = filter[prop];

当使用Object.keys时,以下工作:

Object.keys(this)
.forEach(key => {
console.log(this[key as keyof MyClass]);
});

我用这个:

interface IObjectKeys {
[key: string]: string | number;
}


interface IDevice extends IObjectKeys {
id: number;
room_id: number;
name: string;
type: string;
description: string;
}

注意:"[键:字符串]"是什么?JavaScript中的对象主要是由键值对组成的属性集合。此外,键只能是字符串(即使是数组元素),但值可以是任何数据类型。

如果你在对象中使用可选属性:

interface IDevice extends IObjectKeys {
id: number;
room_id?: number;
name?: string;
type?: string;
description?: string;
}

... 你应该在IObjectKeys接口中添加'undefined'值:

interface IObjectKeys {
[key: string]: string | number | undefined;
}

我对Alex McKay的函数/用法做了一些小更改,我认为这使它更容易理解为什么它可以工作,并且也遵循no-use-before-define规则。

首先,定义这个函数来使用:

const getKeyValue = function<T extends object, U extends keyof T> (obj: T, key: U) { return obj[key] }

在我写它的方式中,函数的泛型首先列出对象,然后是对象上的属性(这些可以以任何顺序出现,但如果你在T extends object之前指定U extends key of T,你就违反了no-use-before-define规则,而且对象在前,属性在后也是有意义的。最后,我使用了更常用的函数语法,而不是箭头操作符(=>)。

不管怎样,通过这些修改,你可以像这样使用它:

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


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


getKeyValue(user, "name")

同样,我觉得这样更有可读性。

我对这个问题做了一个模拟。看来问题是我们该怎么做 在Typescript中使用括号符号动态访问对象属性

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


export default class User {
constructor(private data: IUserProps) {}


get(propName: string): string | number {
return this.data[propName as keyof IUserProps];
}
}


我找到了一个博客,可能有助于更好地理解这一点。

这里是一个链接 https://www.nadershamma.dev/blog/2019/how-to-access-object-properties-dynamically-using-bracket-notation-in-typescript/ < / p >

对于将来偶然发现这个的人:

如果你得到TypeScript错误

“…字符串类型的表达式不能用于索引…

然后简单地指定“string类型的表达式”是关键的,即类型的对象。例如,

const someObj:ObjectType = data;
const field = 'username';


// This gives an error
const temp = someObj[field];


// Solution 1: When the type of the object is known
const temp = someObj[field as keyof ObjectType]


// Solution 2: When the type of the object is not known
const temp = someObj[field as keyof typeof someObj]

我知道这有点晚了,但它所需要的只是添加一个小类型转换,我写了一个静态函数,安全地返回具有正确类型的键数组。你只需要定义类型并将对象作为参数传递:

export class ObjectUtil {
public static getObjectKeys<T>(obj: Object) {
if (!obj) {
return [];
}


return Object.keys(obj).map((key: string) => key as keyof T);
}
}

下面是一个简单的例子:

ObjectUtil.getObjectKeys<Address>(address).forEach((key) => {
console.log(address[key]);
});

这不是对原来问题的回答,而是对这个问题的一般工作。

< p >原始问题: person[cr.field]导致此错误


我正在做一个通用的高级搜索形式,用户可以选择一个字段,比较器和所需的值。当试图根据键从对象中读取值时,我得到这个错误(尽管field值是字符串类型,我认为它应该是好的)

我所做的就是像这样提取[key, value]

const x: [string, any] = Object.entries(person).find(([key, _]) => key === cr.field);

例如,如果我的标准(cr)是{ field: 'name', value: 'John' },字段name实际上存在于person obj中。,它应该返回字段名和值作为元组(x是[string, any]或undef)。如果没有找到,未定义。

public getUserName():字符串{

const accessToken = this.getAccessToken();
const claims:any = this.getUserClaims();
console.log('access token ',accessToken);
this.getUserInfo();
return claims['sub'].split('@')[0];

//给变量任何类型

它适用于我的keyofas操作符:

const keys: [keyof ITrainInfo] = Object.keys(this.trainInfo) as [
keyof ITrainInfo,
]
keys.forEach((property) => {
// console.log(tmpUser[property])
if (this.trainInfo === undefined) return
if (this.trainInfo[property] !== undefined) {
// your code here
/*const trainsToSet = trains.find((field) => field.name === property)
if (trainsToSet != undefined)
trainsToSet.value = this.trainInfo[property]?.toString()
*/
}
})