องค์ประกอบมีประเภท "ใด ๆ " โดยปริยายเนื่องจากไม่สามารถใช้นิพจน์ประเภท "สตริง" เพื่อจัดทำดัชนีได้


99

ลองใช้ 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 ) เนื่องจากมีข้อผิดพลาดคล้ายกัน แต่ฉันพยายามเพิ่มลายเซ็นดัชนีใน พิมพ์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;

คำตอบ:


102

นี้เกิดขึ้นเพราะคุณพยายามที่จะเข้าถึงสถานที่ให้บริการโดยใช้สตริงplotOptions nametypescript เข้าใจว่าnameอาจจะมีค่าใด ๆ ที่ไม่เพียง plotOptionsแต่ชื่อคุณสมบัติจาก ดังนั้น TypeScript จึงต้องเพิ่มลายเซ็นดัชนีให้plotOptionsดังนั้นจึงรู้ว่าคุณสามารถใช้ชื่อคุณสมบัติใดก็ได้ในplotOptions. แต่ฉันขอแนะนำให้เปลี่ยนประเภทnameดังนั้นจึงเป็นได้เพียงplotOptionsคุณสมบัติเดียว

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

ตอนนี้คุณจะสามารถใช้ได้เฉพาะชื่อคุณสมบัติที่มีอยู่ในplotOptions.

คุณต้องเปลี่ยนรหัสของคุณเล็กน้อย

ก่อนอื่นให้กำหนดอาร์เรย์ให้กับตัวแปร temp ดังนั้น 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
}

35
// 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ดังนั้นจึงสามารถใช้เป็นค่าค้นหาได้

นี่คือตัวอย่างทั้งหมด:

ฉันได้เปลี่ยนลำดับของ generics ( U extends keyof Tตอนนี้มาก่อนT extends object) เพื่อเน้นว่าลำดับของ generics นั้นไม่สำคัญและคุณควรเลือกลำดับที่เหมาะสมกับฟังก์ชันของคุณมากที่สุด

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
ฉันเขียนแพ็คเกจ npmเล็ก ๆพร้อมฟังก์ชั่นนี้เพื่อให้งานนี้ง่ายขึ้นสำหรับผู้ที่ยังใหม่กับ typescript
Alex Mckay

แพคเกจมีขนาดประมาณ 38 ไบต์เมื่อบรรจุและลดขนาดแล้ว
Alex Mckay

2
อเล็กซ์ดูเหมือนว่าสเกลของคุณจากแย่ที่สุดไปหาดีที่สุดจะประมาณสเกลจากสั้นและเรียบง่ายไปจนถึงสัญลักษณ์โปรลิกซ์ซึ่งมีเพียงนักมาโซคิสต์เท่านั้นที่สามารถรักได้ ในแง่นี้นี่คือเวอร์ชัน "bester" ซึ่งเป็นเพียงเวอร์ชัน "ดีที่สุด" ของคุณที่ซ้ำและสับ: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
webb

11

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

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]);
    });

5

เมื่อเราทำบางสิ่งเช่น obj [คีย์] typescript ไม่สามารถทราบได้ว่ามีคีย์นั้นอยู่ในวัตถุนั้นหรือไม่ ฉันทำอะไรไป:

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

1

ฉันได้ทำการเปลี่ยนแปลงเล็กน้อยกับฟังก์ชัน / การใช้งานของ 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 objectคุณจะทำลายno-use-before-defineกฎและมันก็สมเหตุสมผล เพื่อให้วัตถุเป็นอันดับแรกและคุณสมบัติของมันในที่สุดฉันใช้ไวยากรณ์ของฟังก์ชันทั่วไปแทนตัวดำเนินการลูกศร (=> )

อย่างไรก็ตามด้วยการปรับเปลี่ยนเหล่านี้คุณสามารถใช้งานได้ดังนี้:

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

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

getKeyValue(user, "name")

ซึ่งอีกครั้งฉันคิดว่าจะอ่านได้มากขึ้น


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];

-12

นี่คือสิ่งที่ได้ผลสำหรับฉัน tsconfig.jsonมีตัวเลือกที่noImplicitAnyว่ามันถูกกำหนดให้trueฉันเพียงแค่ตั้งค่าให้falseและตอนนี้ฉันสามารถเข้าถึงคุณสมบัติในวัตถุที่ใช้สาย


7
การดำเนินการนี้จะเป็นการลบข้อ จำกัด และไม่มีประโยชน์ในการใช้ typescript หากเรายังคงลบข้อ จำกัด เหล่านี้
Joseph Briggs

ฉันไม่เห็นด้วย @JosephBriggs typescript นำมาสู่ประโยชน์อื่น ๆ อีกมากมายในตาราง การตรวจสอบประเภทเป็นหนึ่งในนั้น เป็นเรื่องดีที่คุณสามารถเลือกเข้าร่วมหรือไม่รับได้ขึ้นอยู่กับข้อกำหนดของโครงการ
Zeke

15
วิธีนี้ไม่ได้ช่วยแก้ปัญหา แต่เพียงแค่เพิกเฉย
thedayturns

1
@Zeke ฉันเข้าใจเพื่อน :) ฉันเขียนอย่างเร่งรีบ สิ่งที่ฉันหมายถึงก็คือถ้าเรายังคงแก้ปัญหาโดยเพียงแค่บอกให้เพิกเฉยตั้งแต่แรกก็ไม่มีประเด็นใดเกิดขึ้น แต่แล้วก็ขึ้นอยู่กับโครงการและการตัดสินใจของแต่ละโครงการ
Joseph Briggs

ฉันชอบแบบนี้ ... แม้แต่ภาษาประเภทที่รุนแรงเช่น c # ก็มี "var" การอ้างถึงstackoverflow.com/questions/62377614/… ... มีการออกแบบทางวิศวกรรมเล็กน้อยเพื่อใส่รหัสการชนทั้งหมดเพื่อเข้าถึงคุณสมบัติง่ายๆ
Eric
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.