タイプ「string」の式はインデックス付けに使用できないため、要素には暗黙的に「any」タイプがあります


98

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;

回答:


100

これは、plotOptionsstringを使用してプロパティにアクセスしようとしたために発生しますname。TypeScriptnameは、からのプロパティ名だけでなく、任意の値を持つ可能性があることを理解し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
}

34
// 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
Typescriptを初めて使用する人がこのタスクを簡単に行えるように、この関数を使用して小さなnpmパッケージを作成しました。
AlexMckay20年

パッケージ化および縮小化されると、パッケージは約38バイトになります。
AlexMckay20年

2
アレックス、最悪から最高までのあなたのスケールは、短くて単純なものからプロリックスのシンボルまでのスケールに近いようです-マゾヒストだけが愛することができるサラダ。その精神で、ここに「ベスター」バージョンがあります。これは、複製およびシャッフルされた「最良の」バージョンです。d ee((oent V[jkkncnt=kfay u skk==e>Ugs(< :ds b]uUeT eKne e=x>:dTy: lc,n=b;T=by>o[x oo<U yc ,t Ketgt )c)yo oe eTV aej ; Txb )t> se de esytkjeeUe otj]b j o:oebe)ytlT(eejfs>toyn> x
webb20年

10

私はこれを使用します:

interface IObjectKeys {
  [key: string]: string | number;
}

interface IDevice extends IObjectKeys {
  id: number;
  room_id: number;
  name: string;
  type: string;
  description: string;
}

8

を使用するObject.keysと、次のように機能します。

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

4

このようなことをすると、obj [key] Typescriptは、そのキーがそのオブジェクトに存在するかどうかを確実に知ることができません。私がしたこと:

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

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の関数/使用法にいくつかの小さな変更を加えました。これにより、Alex McKayが機能する理由を理解しやすくなり、定義前に使用ないルールも順守できるようになりました。

まず、使用するこの関数を定義します。

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

私が書いたように、関数のジェネリックは最初にオブジェクトをリストし、次にオブジェクトのプロパティをリストします(これらは任意の順序で発生する可能性がありますが、ルールを破るU extends key of T前に指定すると、それは理にかなっていますオブジェクトを最初に、そのプロパティを2番目に設定します。最後に、矢印演算子()の代わりに、より一般的な関数構文を使用しました。T extends objectno-use-before-define=>

とにかく、これらの変更を加えると、次のように使用できます。

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

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

getKeyValue(user, "name")

繰り返しになりますが、もう少し読みやすくなっています。


-11

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


7
これによりstrictが削除されます。これらの制限を削除し続けると、typescriptを使用しても意味がありません。
ジョセフブリッグス

@JosephBriggsに同意しません。Typescriptは、他にも多くの利点をもたらします。タイプチェックはその1つです。プロジェクトの要件に応じてオプトインまたはオプトアウトできるのは素晴らしいことです。
ジーク

15
これは問題を解決せず、単に無視します。
thedayturns

1
@Zeke私は仲間を理解しています:)私は急いで書いていました。私が言いたかったのは、無視するように言うだけで問題を解決し続けると、そもそもそれは意味がないということです。しかし、繰り返しになりますが、すべてはプロジェクトとプロジェクトごとの決定に依存します。
ジョセフブリッグス

私はこれが好きです... c#のような強い型の言語でさえ「var」を持っています。参照のうえstackoverflow.com/questions/62377614/...を... Aはオーバー単純なプロパティにアクセスするためのコードをぶつけのすべての並べ替えを置くように設計ビット。
エリック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.