为 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]);
到目前为止,我查看了文章“Indexing objects in 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;
这是因为您尝试使用字符串 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
}
对于将来偶然发现此问题的任何人:
如果您收到 TypeScript 错误
'...字符串类型的表达式不能用于索引...'
然后只需指定“字符串类型的表达式”是该对象类型的键。例如,
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]
使用 Object.keys
时,以下工作:
Object.keys(this)
.forEach(key => {
console.log(this[key as keyof MyClass]);
});
key
是 this
实际拥有的东西。事实上,我们甚至可以去掉类名并使用 this
来代替 this[key as keyof this]
!
Object.keys
的元素循环时,key
实际上还能是什么? - 在未来的 TypeScript 版本中,我猜可能不需要这种类型转换。
// 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];
我用这个:
interface IObjectKeys {
[key: string]: string | number;
}
interface IDevice extends IObjectKeys {
id: number;
room_id: number;
name: string;
type: string;
description: string;
}
注意:“[key:string]”是什么? JavaScript 中的对象主要是由键值对组成的属性集合。此外,键只能是字符串(即使是数组元素),但值可以是任何数据类型。
如果您在对象中使用可选属性:
interface IDevice extends IObjectKeys {
id: number;
room_id?: number;
name?: string;
type?: string;
description?: string;
}
...您应该将“未定义”值添加到 IObjectKeys 接口中:
interface IObjectKeys {
[key: string]: string | number | undefined;
}
room_id?:number;
?optional
这意味着可能是 undefined
,所以我应该像这样添加它 [key: string]: string | number | undefined
。解决了问题Property 'room_id' of type 'number | undefined' is not assignable to string index type 'string | number'.
:)
我已经模拟了这个问题。看起来问题是我们应该如何在 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];
}
}
我发现了一个可能有助于更好地理解这一点的博客。
当我们执行 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];
没有 typescript
错误
const formData = new FormData();
Object.keys(newCategory).forEach((k,i)=>{
var d =Object.values(newCategory)[i];
formData.append(k,d)
})
我对 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
,则会破坏 {3 } 规则,而且首先有对象,然后是它的属性才有意义。最后,我使用了更常见的函数语法而不是箭头运算符(=>
)。
无论如何,通过这些修改,您可以像这样使用它:
interface User {
name: string;
age: number;
}
const user: User = {
name: "John Smith",
age: 20
};
getKeyValue(user, "name")
再一次,我发现它更具可读性。
它适用于 keyof
和 as
运算符:
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()
*/
}
})
这不是对原始问题的答案,而是针对此问题的通用解决方法。
原始问题: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)。如果未找到,则未定义。
公共getUserName():字符串{
const accessToken = this.getAccessToken();
const claims:any = this.getUserClaims();
console.log('access token ',accessToken);
this.getUserInfo();
return claims['sub'].split('@')[0];
}
//给变量任何类型
我知道这有点太晚了,但只需要添加一点类型转换,我编写了一个静态函数,可以安全地返回具有正确类型的键数组。您只需要定义类型并将对象作为参数传递:
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]);
});
这对我有用。 tsconfig.json
有一个选项 noImplicitAny
,它被设置为 true
,我只是简单地将它设置为 false
,现在我可以使用字符串访问对象中的属性。
var
与 Typescript 中的 any
不同。如果无法推断类型,var
将无法在像 C#
这样的强类型语言中工作,而 any
允许变量随时采用任何类型 - any
变量可以重新分配给完全不同类型的值。这在 C#
中是不可能的。
{[key: string]: boolean}
这很有帮助,非常直观