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

ฉันจะป้องกันข้อผิดพลาด "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];
}

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