ค้นหาเว็บไซต์

typescript - อิลิเมนต์มีประเภท 'any' โดยปริยายเนื่องจากนิพจน์ของประเภท 'string' ไม่สามารถใช้เป็นตัวชี้ดัชนีได้


กำลังลองใช้ TypeScript สำหรับโปรเจกต์ React และฉันติดปัญหากับข้อผิดพลาดนี้:

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) เพราะว่ามีข้อผิดพลาดที่คล้ายกัน แต่ฉันพยายามเพิ่ม index signature ให้กับ type 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;

คำตอบ #1

สำหรับใครก็ตามที่พบบันทึกนี้ในอนาคต:

ถ้าคุณได้รับข้อผิดพลาด TypeScript

'...ไม่สามารถใช้ค่าประเภทสตริงเป็นดัชนีได้...'

จากนั้นเพียงระบุว่า 'expression ของชนิด string' เป็น คีย์ของ ชนิดของ วัตถุนั้น ตัวอย่างเช่น,

const someObj:ObjectType = data;
const field = 'username';

// This gives an error
const temp = someObj[field];

// Solution 1: When the type of the object is known
const temp = someObj[field as keyof ObjectType]

// Solution 2: When the type of the object is not known
const temp = someObj[field as keyof typeof someObj]

คำตอบ #2

สิ่งนี้เกิดขึ้นเพราะคุณพยายามเข้าถึงคุณสมบัติ plotOptions โดยใช้สตริง name TypeScript เข้าใจว่า name อาจมีค่าใดก็ได้ ไม่ใช่แค่ชื่อคุณสมบัติจาก plotOptions ดังนั้น TypeScript จึงต้องการให้เพิ่มสัญลักษณ์ดัชนี (index signature) ให้กับ 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 และไม่มีวิธีตรวจสอบชนิดของ props ในเวลาคอมไพล์ วิธีเดียวคือการเพิ่ม index signature ให้กับ plotOptions ของคุณ:

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

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

คำตอบ #3

เมื่อใช้ Object.keys สิ่งต่อไปนี้สามารถทำงานได้:

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

คำตอบ #4

ฉันแก้ปัญหานี้โดยใช้ keyof

messageMap = {
 "Hi": "Hello",
 "Who are you": "My name is Test Sat Bot",
 "What is your role": "Just guide for the user",
}

นี่ ❌

let answer = this.messageMap[question];

แทนที่ด้วย ✔️

let answer = this.messageMap[question as keyof typeof this.messageMap];

ที่นี่ question เป็นชนิดของ string

getBotMessage(question: string){    
  let answer = this.messageMap[question as keyof typeof this.messageMap];
}

คำตอบ #5

ฉันใช้สิ่งนี้:

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

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

หมายเหตุ: "[key: string]" คืออะไร? อ็อบเจกต์ใน JavaScript เป็นเพียงชุดของ properties ที่ประกอบด้วยคู่กุญแจ-ค่า นอกจากนี้ กุญแจสามารถเป็นเพียง สตริง เท่านั้น (แม้กระทั่งสำหรับสมาชิกของอาร์เรย์) แต่ค่าสามารถเป็นได้ทุกประเภทข้อมูล

ถ้าคุณใช้คุณสมบัติที่เป็นทางเลือกในวัตถุของคุณ:

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

... คุณควรเพิ่มค่าที่ 'undefined' ลงในอินเทอร์เฟซ IObjectKeys:

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

คำตอบ #6

// 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/value (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];

คำตอบ #7

ฉันได้สร้างการจำลองปัญหา ดูเหมือนว่าปัญหาคือวิธีที่เราควร เข้าถึงคุณสมบัติของวัตถุแบบไดนามิกโดยใช้สัญลักษณ์วงเล็บใน Typescript

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

export default class User {
  constructor(private data: IUserProps) {}

  get(propName: string): string | number {
    return this.data[propName as keyof IUserProps];
  }
}

ฉันพบบล็อกที่อาจช่วยให้เข้าใจเรื่องนี้ได้ดียิ่งขึ้น

นี่คือลิงก์ https://www.nadershamma.dev/blog/2019/how-to-access-object-properties-dynamically-using-bracket-notation-in-typescript/

คำตอบ #8

เมื่อเราทำบางสิ่งแบบนี้ obj[key] Typescript ไม่สามารถรู้ได้แน่ชัดว่าคีย์นั้นมีอยู่ในอ็อบเจ็กต์นั้นหรือไม่ สิ่งที่ฉันทำ:

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

คำตอบ #9

นี่อาจช่วยใครบางคนได้ ก่อนอื่น อ็อบเจ็กต์ว่างต้องมีชนิดเป็น Record<string, any> เพื่อให้มันสามารถรับคีย์เป็น string และค่าต่างๆ เป็น any

const sortFields: Record<string, any> = {};
const keyName = `field`;

sortFields[keyName] = 'asc';

ดังนั้นตรงนี้เมื่อเราบันทึก sortFields

 console.log("sortFields==== ", sortFields); // sortFields==== {field: 'asc'}

หรือถ้าเราต้องการเพิ่มมากกว่าหนึ่งองค์ประกอบในวัตถุ เราสามารถทำได้แบบนี้

sortFields.email = "asc"; // Or sortFields["email"] = "asc"; but its better written in dot notation.
sortFields.name = "desc";
sortFields.phone = "desc";

ดังนั้นตอนนี้เมื่อเราเข้าสู่ระบบ sortFields

 console.log("sortFields==== ", sortFields); // sortFields==== {field: 'asc', email: 'asc', name: 'desc', phone: 'desc'}

บทความที่เกี่ยวข้อง