元素隐式地具有“ any”类型,因为类型“ string”的表达式不能用于索引


85

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

Answers:


82

发生这种情况是因为您尝试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;
}

现在,您将只能使用中存在的属性名称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
}

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

1
我编写了一个带有此功能的小型npm软件包,以使Typescript新手可以轻松完成此任务。
Alex Mckay

打包并缩小后,该包大约为38个字节。
Alex Mckay

4

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

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


2

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

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

0

没有typescript错误

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


0

我对Alex McKay的功能/用法进行了一些小改动,我认为这样可以更轻松地了解其工作原理,并遵循定义前的无用规则。

首先,定义要使用的功能:

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

按照我编写代码的方式,该函数的泛型首先列出对象,然后在对象上列出属性(这些可以以任何顺序发生,但是如果U extends key of TT extends object违反no-use-before-define规则之前指定,这也很有意义首先具有对象,其属性具有第二个对象。最后,我使用了更通用的函数语法代替了箭头运算符(=>)。

无论如何,通过这些修改,您可以像这样使用它:

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

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

getKeyValue(user, "name")

同样,我发现它更具可读性。


-9

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


6
这将删除严格,如果我们继续删除这些限制,那么使用打字稿就没有意义了。
约瑟夫·布里格斯

我不同意@JosephBriggs。Typescript带来了许多其他好处。类型检查是其中之一。很高兴您可以根据项目的要求选择加入或退出。
Zeke

13
这不能解决问题,而只是忽略它。
dayturns

1
@Zeke我了解队友:)我当时急于写作。我的意思是,如果我们仅通过告诉它忽略而继续解决问题,那么它就毫无意义。但是,这又取决于项目以及每个项目的决策。
约瑟夫·布里格斯

我更喜欢...甚至像c#这样的强类型语言也具有“ var”。引用stackoverflow.com/questions/62377614/… ...有点过分设计,无法放入各种碰撞代码来访问简单属性。
埃里克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.