尝试一个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;


当前回答

当使用Object时。按键,以下作品:

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

其他回答

TypeScript需要确保这些值存在于trainInfo中,否则它将全部读取为字符串

而不是这样做

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

这样做

interface trainInfo {
  name: "train_1" | "train_2" | "train_3"| "train_4";
  x: Array<number>;
  y: Array<number>;
  type: string;
  mode: string;
}

我对这个问题做了一个模拟。看来问题是我们该怎么做 在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/

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

坏-错误的原因是对象类型默认为空对象。因此,不可能使用字符串类型来索引{}。

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

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

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

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]);
});

当使用Object时。按键,以下作品:

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