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