typescript - มีใครอธิบายได้ไหมว่าทำไมสิ่งนี้ถึงใช้ไม่ได้?
แนวคิดคือการบังคับไม่ให้ใช้ฟิลด์บางอย่างในอ็อบเจ็กต์ที่ส่งไปยังฟังก์ชัน และคุณสามารถใช้สิ่งอื่นใดก็ได้ (นั่นคือเหตุผลที่ใช้ extends)
type ForbiddenFields = 'a' | 'b' | 'c';
type Data = { [K in ForbiddenFields]?: never };
function foobar<T extends Data>(data: T): void {
//...
}
const obj = { x: 1, y: 2 };
foobar(obj); // Type '{ x: 1, y: 2 }' has no properties in common with type Data
ทำไมฉันถึงได้
ชนิด ' { x: 1, y: 2 } ' ไม่มีคุณสมบัติที่เหมือนกันกับชนิด Data
ถ้าฟังก์ชันนั้นคาดหวังเพียงออบเจกต์ที่ สืบทอด จาก Data จะไม่เหมือนกับการพูดว่า <T extends {}> แต่แค่กำหนดว่า ถ้า ForbiddenField ถูกระบุ ต้องเป็น never -- ดังนั้นจะไม่สามารถมีอยู่ได้หรือ?
คำตอบ #1
ฉันไม่สามารถบอกได้ว่าทำไม Typescript ถึงไม่พิจารณา { x: number, y: number } ว่าเป็นการขยาย { a?: never } - ด้วยเหตุผลบางอย่างมันจึงต้องการให้มีคีย์ 'a' อยู่ในประเภทแรกด้วยเหตุผลบางอย่าง
อย่างไรก็ตาม การแก้ปัญหาของคุณต้องการเพียงการปรับเปลี่ยนลักษณะฟังก์ชันอย่างง่าย:
function foobar<T>(data: T & Data): voidคำตอบ #2
นี่คือการละเมิดการตรวจจับประเภทแบบอ่อนที่ถูกเพิ่มเข้ามาใน TypeScript 2.4 เนื่องจากคุณสมบัติของ Data เป็น ตัวเลือกทั้งหมด คอมไพเลอร์จึงพิจารณาว่าเป็น "ประเภทอ่อน" และดังนั้นจึงไม่เข้ากันได้กับประเภทอื่นใด เว้นแต่ว่าจะมีอย่างน้อยหนึ่งคุณสมบัติที่ทับซ้อนกัน เรื่องนี้ เหมือนกับการตรวจจับคุณสมบัติเกินของอ็อบเจ็กต์ลิเทอรัล ไม่ใช่จริงๆ คุณสมบัติความถูกต้องของระบบประเภท แต่เป็นเหมือนกฎลินเตอร์ที่หยุดสิ่งที่มันคิดว่าน่าจะเป็นข้อผิดพลาด
บันทึกการปล่อยสำหรับการตรวจจับชนิดอ่อนระบุว่า:
เนื่องจากนี่เป็นการเปลี่ยนแปลงที่สำคัญ คุณอาจจำเป็นต้องรู้เกี่ยวกับวิธีแก้ปัญหาชั่วคราว ซึ่งเหมือนกับวิธีแก้ปัญหาสำหรับการตรวจสอบวัตถุแบบเข้มงวด:
- ประกาศคุณสมบัติถ้ามันมีอยู่จริง
- เพิ่มลายเซ็นดัชนีให้กับชนิดอ่อน (เช่น
[propName: string]: {})- ใช้การยืนยันประเภท (เช่น
opts as Options)
เราสามารถใช้ข้อเสนอแนะข้อที่สองและเพิ่มสัญลักษณ์ดัชนีสตริงได้ เนื่องจากเราไม่ต้องการจำกัดคุณสมบัติอื่นๆ เหล่านี้ทั้งหมด เราน่าจะทำให้ค่ามีประเภท any (เพราะแม้แต่ประเภทที่ปลอดภัย unknown ก็จะทำให้ประเภทอินเทอร์เฟซที่ไม่มีสัญลักษณ์ดัชนีถูกตัดออก ซึ่งเราไม่ต้องการให้เกิดขึ้น) มันอาจดูเหมือนดังนี้:
interface AnyData extends Data {
[k: string]: any;
}
function foobar<T extends AnyData>(data: T): void {
//...
}
และคุณสามารถตรวจสอบได้ว่าทุกอย่างทำงานตามที่ต้องการ:
const obj = { x: 1, y: 2 };
foobar(obj); // okay
const obj2 = { x: 1, y: 2, c: 3 };
foobar(obj2); // error! Types of property 'c' are incompatible.
interface Baz {
x: 1,
y: 2
}
declare const baz: Baz;
foobar(baz); // okay
ลิงก์ Playground ไปยังโค้ด
