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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/57086672/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 08:04:03  来源:igfitidea点击:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index

reactjstypescript

提问by DLee

Trying out TypeScript for a React project and I'm stuck on this error:

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

Which appears when I try to filter the array in my component

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

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

So far I looked at the article "Indexing objects in TypeScript" (https://dev.to/kingdaro/indexing-objects-in-typescript-1cgi) since it had a similar error, but I tried to add the index signature to type plotTypesand I still get the same error.

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

My component code:

我的组件代码:

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;

回答by Fyodor

This happens because you try to access plotOptionsproperty using string name. TypeScript understands that namemay have any value, not only property name from plotOptions. So TypeScript requires to add index signature to plotOptions, so it knows that you can use any property name in plotOptions. But I suggest to change type of name, so it can only be one of plotOptionsproperties.

发生这种情况是因为您尝试plotOptions使用 string访问属性name。TypeScript 理解它name可能具有任何值,而不仅仅是来自plotOptions. 所以TypeScript 需要给. 添加索引签名plotOptions,所以它知道你可以在plotOptions. 但我建议更改 的类型name,因此它只能是plotOptions属性之一。

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

Now you'll be able to use only property names that exist in plotOptions.

现在,您将只能使用存在于plotOptions.

You also have to slightly change your code.

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

First assign array to some temp variable, so TS knows array type:

首先将数组分配给某个临时变量,以便 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"
    },
    // ...
}

Then filter:

然后过滤:

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

If you're getting data from API and have no way to type check props at compile time the only way is to add index signature to your plotOptions:

如果您从 API 获取数据并且无法在编译时键入检查道具,唯一的方法是将索引签名添加到您的plotOptions:

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

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

回答by Alex Mckay

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

Bad - the reason for the error is the objecttype is just an empty object by default. Therefore it isn't possible to use a stringtype to index {}.

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

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

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

Best - Textends empty object. Uextends the keys of T. Therefore Uwill always exist on T, therefore it can be used as a look up value.

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

Here is a full example:

这是一个完整的例子:

I have switched the order of the generics (U extends keyof Tnow comes before T extends object) to highlight that order of generics is not important and you should select an order that makes the most sense for your function.

我已经切换了泛型的顺序(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'

回答by Zeke

This is what it worked for me. The tsconfig.jsonhas an option noImplicitAnythat it was set to true, I just simply set it to falseand now I can access properties in objects using strings.

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