ฉันจะป้องกันข้อผิดพลาด "Index signature ของประเภทวัตถุมีประเภท 'any' โดยปริยาย" ได้อย่างไรเมื่อคอมไพล์ TypeScript โดยเปิดใช้แฟลก noImplicitAny?
ฉันมักจะแปลง TypeScript พร้อมกับแฟล็ก --noImplicitAny สิ่งนี้สมเหตุสมผลเพราะฉันต้องการให้การตรวจสอบประเภทของฉันเข้มงวดที่สุดเท่าที่จะทำได้
ปัญหาของฉันคือด้วยโค้ดด้านล่างนี้ฉันได้รับข้อผิดพลาด:
Index signature of object type implicitly has an 'any' type
interface ISomeObject {
firstKey: string;
secondKey: string;
thirdKey: string;
}
let someObject: ISomeObject = {
firstKey: 'firstValue',
secondKey: 'secondValue',
thirdKey: 'thirdValue'
};
let key: string = 'secondKey';
let secondValue: string = someObject[key];
สิ่งที่สำคัญที่ควรสังเกตคือแนวคิดคือค่าตัวแปรหลักมาจากที่อื่นในแอปพลิเคชันและสามารถเป็นค่าของกุญแจใดก็ได้ในวัตถุ
ฉันได้พยายามแปลงประเภทโดยชี้แจงอย่างชัดเจนดังนี้:
let secondValue: string = <string>someObject[key];
หรือว่าสถานการณ์ของฉันเป็นไปไม่ได้เพียงเพราะใช้ --noImplicitAny?
คำตอบ #1
การเพิ่มลายเซ็นดัชนีจะทำให้ TypeScript รู้ว่าประเภทควรเป็นอะไร
ในกรณีของคุณนั่นจะเป็น [key: string]: string;
interface ISomeObject {
firstKey: string;
secondKey: string;
thirdKey: string;
[key: string]: string;
}
อย่างไรก็ตาม สิ่งนี้ยังบังคับให้ประเภทของคุณสมบัติทั้งหมดตรงกับลายเซ็นดัชนี เนื่องจากคุณสมบัติทั้งหมดเป็น string จึงสามารถทำงานได้
แม้ว่าสัญลักษณ์ดัชนีจะเป็นวิธีที่ทรงพลังในการอธิบายรูปแบบของอาร์เรย์และ 'พจนานุกรม' แต่ก็ยังบังคับให้คุณสมบัติทั้งหมดตรงกับประเภทการคืนค่าของพวกมันด้วย
แก้ไข :
ถ้าประเภทไม่ตรงกัน สามารถใช้ชนิดยูเนียนได้ [key: string]: string|IOtherObject;
ด้วยประเภทยูเนียน จะดีกว่าถ้าคุณปล่อยให้ TypeScript สรุปประเภทแทนที่จะกำหนดเอง
// Type of `secondValue` is `string|IOtherObject`
let secondValue = someObject[key];
// Type of `foo` is `string`
let foo = secondValue + '';
แม้ว่าสิ่งนั้นอาจทำให้ยุ่งเหยิงเล็กน้อยหากคุณมีชนิดต่าง ๆ จำนวนมากในลายเซ็นดัชนี ทางเลือกสำหรับสิ่งนั้นคือการใช้ any ในลายเซ็น [key: string]: any; จากนั้นคุณจะต้องทำการแปลงชนิดข้อมูลเหมือนที่คุณทำข้างต้น
คำตอบ #2
อีกวิธีหนึ่งในการหลีกเลี่ยงข้อผิดพลาดคือการใช้การแปลงชนิดแบบนี้:
let secondValue: string=(<any>someObject)[key]; (สังเกตวงเล็บ)
ปัญหาเพียงอย่างเดียวคือสิ่งนี้ไม่ปลอดภัยตามประเภทอีกต่อไป เนื่องจากคุณกำลังแปลงเป็น any แต่คุณสามารถแปลงกลับไปยังประเภทที่ถูกต้องได้เสมอ
ป.ล. ฉันกำลังใช้ TypeScript 1.7 ไม่แน่ใจเกี่ยวกับเวอร์ชันก่อนหน้านี้
คำตอบ #3
TypeScript 2.1 แนะนำวิธีที่สวยงามในการจัดการปัญหานี้
const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];
เราสามารถเข้าถึงชื่อคุณสมบัติของวัตถุทั้งหมดในระหว่างขั้นตอนการคอมไพล์โดยใช้คำสำคัญ keyof (ดูบันทึกการเปลี่ยนแปลง)
คุณเพียงแค่ต้องแทนที่ประเภทตัวแปร string ด้วย keyof ISomeObject ตอนนี้ตัวคอมไพเลอร์รู้ว่า ตัวแปร key สามารถเก็บชื่อคุณสมบัติจาก ISomeObject ได้เท่านั้น
ตัวอย่างเต็ม:
interface ISomeObject {
firstKey: string;
secondKey: string;
thirdKey: number;
}
const someObject: ISomeObject = {
firstKey: 'firstValue',
secondKey: 'secondValue',
thirdKey: 3
};
const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];
// You can mix types in interface, keyof will know which types you refer to.
const keyNumber: (keyof ISomeObject) = 'thirdKey';
const numberValue: number = someObject[keyNumber];
โค้ดสดบน typescriptlang.org (ตั้งค่า noImplicitAny)
การอ่านเพิ่มเติมกับการใช้งาน keyof มากขึ้น
คำตอบ #4
การตั้งค่า tsconfig ต่อไปนี้จะทำให้คุณละเว้นข้อผิดพลาดเหล่านี้ได้ - ตั้งค่าเป็น true
ระงับข้อผิดพลาดดัชนีแบบไม่ระบุประเภทโดยปริยาย
ระงับข้อผิดพลาด noImplicitAny สำหรับการจัดทำดัชนีวัตถุที่ขาดลายเซ็นดัชนี
คำตอบ #5
ใช้ keyof typeof
const cat = {
name: 'tuntun'
}
const key: string = 'name'
cat[key as keyof typeof cat]
คำตอบ #6
โซลูชัน 'keyof' ที่กล่าวถึงข้างต้นใช้งานได้ แต่ถ้าตัวแปรนั้นถูกใช้เพียงครั้งเดียว เช่น การวนลูปผ่านออบเจ็กต์ ฯลฯ คุณก็สามารถทำการแคสต์ประเภทมันได้เช่นกัน
for (const key in someObject) {
sampleObject[key] = someObject[key as keyof ISomeObject];
}
