元素隐式具有“任何”类型,因为“字符串”类型的表达式不能用于索引

IT技术 reactjs typescript
2021-03-23 23:51:51

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

6个回答

发生这种情况是因为您尝试plotOptions使用 string访问属性nameTypeScript 理解它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 获取数据并且无法在编译时键入检查props,唯一的方法是将索引签名添加到您的plotOptions

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

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

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

Object.keys(this)
    .forEach(key => {
      console.log(this[key as keyof MyClass]);
    });
太感谢了!!花了很多时间在这上面
2021-06-06 23:51:51
优秀的!为什么这可以解决问题?
2021-06-11 23:51:51
@Andru 这解决了这个问题,因为 TS 认识到这keythis实际拥有的东西事实上,我们甚至可以去掉类名而使用thislike this[key as keyof this]!
2021-06-12 23:51:51
@ChristosLytras 我明白了。可惜 TypeScript 必须通过类型转换来告知这一点。..key当它是 的元素循环时,实际上还有Object.keys什么?- 我猜在未来的 TypeScript 版本中可能不需要这样的类型转换。
2021-06-12 23:51:51
// 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];
打包和缩小后,该包大约为 38 字节。
2021-06-16 23:51:51
用这个函数写了一个很小的npm 包,让那些不熟悉 Typescript 的人更容易完成这项任务。
2021-06-17 23:51:51

我用这个:

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;
2021-05-23 23:51:51
@GennadyMagomaev 您好,我遇到了同样的问题,您的回答对我帮助很大!我不完全理解这意味着什么:“[key: string]: string | number”。这是否意味着对象的键可以是字符串,在“:”之后是否意味着它也可以是字符串或数字或?我很困惑......你能不能给我解释一下?
2021-06-05 23:51:51
你可以这样做。我认为这与这个问题无关。
2021-06-07 23:51:51
?optional,这意味着undefined我应该像这样添加它[key: string]: string | number | undefined解决了这个问题Property 'room_id' of type 'number | undefined' is not assignable to string index type 'string | number'.:)
2021-06-08 23:51:51
原来如此 :)。你说的对!:) 谢谢!
2021-06-10 23:51:51

对于将来偶然发现此问题的任何人:

如果您收到 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]
这个节目“类型'任何'不能分配到类型'从不”错误
2021-05-29 23:51:51
完美的!someObj[field as keyof typeof someObj]是我要找的
2021-06-14 23:51:51
@Franklin'jGil'z 你能分享一个片段吗?如果您隐式或显式指定了类型,则不应出现该错误。
2021-06-16 23:51:51