'문자열'유형의 표현식을 색인화하는 데 사용할 수 없기 때문에 요소에 암시 적으로 '모든'유형이 있습니다.


85

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;

답변:


83

이것은 plotOptionsstring을 사용하여 속성 에 액세스하려고하기 때문에 발생 합니다 name. TypeScript name는의 속성 이름뿐만 아니라 모든 값을 가질 수 있음을 이해합니다 plotOptions. 따라서 TypeScript는에 인덱스 서명을 추가해야하므로 .NET plotOptions에서 모든 속성 이름을 사용할 수 있음을 알고 있습니다 plotOptions. 그러나 유형을 변경하는 것이 name좋으므로 plotOptions속성 중 하나만 될 수 있습니다.

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에서 데이터를 가져오고 컴파일 타임에 check props를 입력 할 방법이없는 경우 유일한 방법은에 인덱스 서명을 추가하는 것입니다 plotOptions.

type tplotOptions = {
    [key: string]: boolean
}

const plotOptions: tplotOptions = {
    train_1: true,
    train_2: true,
    train_3: true,
    train_4: true
}

29
// 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 패키지 를 작성했습니다.
Alex Mckay

패키지는 일단 패키지화되고 축소되면 약 38 바이트입니다.
Alex Mckay

4

우리가 이런 일을 할 때 obj [key] Typescript는 그 키가 그 객체에 존재하는지 확실히 알 수 없습니다. 제가 한:

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

4

나는 이것을 사용한다 :

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

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

2

를 사용 Object.keys하면 다음이 작동합니다.

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

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 덕분에 저는 소품을 동적으로 설정하는 결심을했습니다.

  for(let prop in filter)
      (state.filter as Record<string, any>)[prop] = filter[prop];

0

Alex McKay의 기능 / 사용에 약간의 변경을 가하여 작동 이유를 좀 더 쉽게 파악하고 정의 전 사용 금지 규칙을 준수합니다 .

먼저 사용할이 함수를 정의합니다.

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

내가 작성한 방식에서 함수의 제네릭은 먼저 객체를 나열한 다음 객체의 속성을 두 번째로 나열합니다 (이러한 순서는 임의의 순서로 발생할 수 있지만 규칙 을 위반 U extends key of T하기 전에 지정 하면 의미가 있습니다. 객체를 먼저, 속성을 두 번째로 사용합니다. 마지막으로 화살표 연산자 ( ) 대신 더 일반적인 함수 구문을 사용했습니다 .T extends objectno-use-before-define=>

어쨌든, 이러한 수정을 통해 다음과 같이 사용할 수 있습니다.

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

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

getKeyValue(user, "name")

다시 말하지만 좀 더 읽기 쉽습니다.


-9

이것이 나를 위해 일한 것입니다. 는 tsconfig.json옵션이 noImplicitAny이 설정되었다는 것을 true, 나는 단지로 설정을 false하고 지금은 문자열을 사용하여 개체의 속성에 액세스 할 수 있습니다.


6
이것은 엄격함을 제거 할 것이며, 이러한 제한을 계속 제거하면 typescript를 사용할 필요가 없습니다.
Joseph Briggs

@JosephBriggs에 동의하지 않습니다. Typescript는 다른 많은 이점을 제공합니다. 유형 검사는 그중 하나입니다. 프로젝트의 요구 사항에 따라 옵트 인 또는 옵트 인 할 수 있다는 점이 좋습니다.
Zeke

13
이것은 문제를 해결하지 않고 그냥 무시합니다.
thedayturns

1
@Zeke 나는 친구를 이해한다 :) 나는 서두르는 글을 쓰고 있었다. 내가 의미하는 바는 우리가 그냥 무시하라고 말함으로써 문제를 계속 해결한다면 애초에 문제가되지 않는다는 것입니다. 그러나 다시 모든 것은 프로젝트와 프로젝트 별 결정에 달려 있습니다.
Joseph Briggs

나는 이것을 선호한다. 심지어 C #과 같은 강력한 타입 언어에도 "var"가있다. 다스 려 stackoverflow.com/questions/62377614/... ... A를 통해 액세스 할 수있는 간단한 속성을 코드를 부딪 치는 모든 종류의를 넣어 설계 비트.
Eric
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.