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

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 ไปยังโค้ด

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