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;
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
}
// 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.
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.