web-dev-qa-db-fra.com

L'élément a implicitement un type 'any' car l'expression de type 'string' ne peut pas être utilisée pour indexer

Essayer TypeScript pour un projet React et je suis bloqué sur cette erreur:

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

Qui apparaît lorsque j'essaie de filtrer le tableau dans mon composant

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

Jusqu'à présent, j'ai regardé l'article "Indexation d'objets en TypeScript" ( https://dev.to/kingdaro/indexing-objects-in-TypeScript-1cgi ) car il contenait une erreur similaire, mais j'ai a essayé d'ajouter la signature d'index au type plotTypes et j'obtiens toujours la même erreur.

Mon code composant:

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

Cela se produit car vous essayez d'accéder à la propriété plotOptions à l'aide de la chaîne name. TypeScript comprend que name peut avoir n'importe quelle valeur, pas seulement le nom de propriété de plotOptions. Donc, TypeScript nécessite d'ajouter une signature d'index à plotOptions, afin qu'il sache que vous pouvez utiliser n'importe quel nom de propriété dans plotOptions. Mais je suggère de changer le type de name, donc ce ne peut être qu'une des propriétés de plotOptions.

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

Vous pourrez désormais utiliser uniquement les noms de propriété qui existent dans plotOptions.

Vous devez également modifier légèrement votre code.

Attribuez d'abord un tableau à une variable temporaire, afin que TS connaisse le type de tableau:

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"
    },
    // ...
}

Filtrez ensuite:

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

Si vous obtenez des données de l'API et n'avez aucun moyen de taper les accessoires de vérification au moment de la compilation, la seule façon est d'ajouter la signature d'index à votre 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];

Mauvais - la raison de l'erreur est que le type object est juste un objet vide par défaut. Il n'est donc pas possible d'utiliser un type string pour indexer {}.

Mieux - la raison pour laquelle l'erreur disparaît est parce que maintenant nous disons au compilateur que l'argument obj sera une collection de chaîne/valeur (string/any) paires. Cependant, nous utilisons le type any, donc nous pouvons faire mieux.

Meilleur - T étend un objet vide. U étend les clés de T. Par conséquent, U existera toujours sur T, il peut donc être utilisé comme valeur de recherche.

3
Alex Mckay

C'est ce qui a fonctionné pour moi. Le tsconfig.json a une option noImplicitAny qui a été définie sur true, je la mets simplement à false et maintenant je peux accéder aux propriétés des objets à l'aide de chaînes.

0
Zeke