web-dev-qa-db-ja.com

タイプ 'string'の式はインデックスに使用できないため、要素は暗黙的に 'any'タイプを持っています

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;

24
DLee

これは、文字列plotOptionsを使用してnameプロパティにアクセスしようとしたために発生します。 TypeScriptは、nameからのプロパティ名だけでなく、plotOptionsに任意の値を設定できることを理解しています。 TypeScriptではplotOptionsにインデックスシグネチャを追加する必要があるため、plotOptionsで任意のプロパティ名を使用できることがわかります。ただし、nameのタイプを変更することをお勧めします。そのため、これはplotOptionsプロパティの1つのみにすることができます。

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
}
23
Fyodor
// 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は空のオブジェクトを拡張します。 UTのキーを拡張します。したがって、Uは常にTに存在するため、ルックアップ値として使用できます。

3
Alex Mckay

これは私のために働いたものです。 tsconfig.jsonにはオプションnoImplicitAnyがあり、trueに設定されていました。これをfalseに設定するだけで、文字列を使用してオブジェクトのプロパティにアクセスできます。

0
Zeke