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

typescript - ข้อผิดพลาด 'ไม่สามารถกำหนดค่าให้กับพารามิเตอร์ประเภท' กับวัตถุเลียนแบบ


มีปัญหากับประเภทวัตถุลิเทอรัล

interface OptionalFoo {
  foo?: number;
}

interface Bar {}

function foobarFn(foobar: OptionalFoo & Bar) {}

foobarFn({ bar: 1 }); // error

foobarFn({ bar: 1 } as { bar: number }); // ok

foobarFn({ bar: 1 } as { bar: 1 }); // ok!

ค่าตัวอ็อบเจ็กต์ที่กำหนดโดยตรงพร้อมชนิดที่อนุมานทำให้เกิดข้อผิดพลาดของชนิด:

อาร์กิวเมนต์ของประเภท '{ bar: number; }' ไม่สามารถกำหนดให้กับพารามิเตอร์ของประเภท 'OptionalFoo & Bar'

แต่ปัญหาไม่ได้อยู่ที่การสรุปผลเอง:

const bar = { bar: 1 }; // inferred { bar: number; }
foobarFn(bar); // ok!?

ปัญหาเดียวกันกับซินแท็กซ์ spread เทียบกับ Object.assign:

foobarFn({...{ bar: 1 }}); // error

foobarFn(Object.assign({}, { bar: 1 })); // ok!?

มีวิธีใดที่จะบรรลุพฤติกรรมด้วยชนิดวัตถุแบบอนุมาน (การตรวจสอบคุณสมบัติเกิน) โดยไม่ใช้วัตถุ literal ตรงที่อยู่ เช่น กับตัวแปร bar หรือการเรียกฟังก์ชันอย่าง Object.assign({ bar: 1 }) หรือไม่?

คำตอบ #1

เพื่อให้ชัดเจน นี่ไม่ได้เกี่ยวกับการตรวจสอบคุณสมบัติเกินเพียงอย่างเดียว การตรวจสอบคุณสมบัติเกินจะเกิดขึ้นเมื่อเรากำหนดวัตถุโดยตรงไปยังตำแหน่งหนึ่ง ในกรณีของคุณ พฤติกรรมที่น่าประหลาดใจมากขึ้นเกิดขึ้นเมื่อกำหนดวัตถุโดยอ้อม ซึ่งโดยปกติจะถูกอนุญาตภายใต้การตรวจสอบคุณสมบัติเกิน

function foo(o: { bar: number }) { }
foo({ bar: 0, foo: "" }) // error direct assignment
foo({ bar:0, foo: ""} as { bar:0, foo: "" }) // ok indirect

ส่วนที่น่าประหลาดใจ อย่างน้อยสำหรับฉันคือ การตรวจสอบอีกประเภทหนึ่ง (การตรวจสอบประเภทแบบอ่อน) ไม่สามารถจับความผิดพลาดนี้ได้ ภายใต้การตรวจสอบประเภทแบบอ่อน (ตามที่อธิบายไว้ที่นี่) หากชนิดมีคุณสมบัติเสริมเพียงอย่างเดียว และเราพยายามกำหนดชนิดที่ไม่มีคุณสมบัติใดร่วมกัน เราควรได้รับข้อผิดพลาด:

function foo(o: { bar?: number }) { }
foo({ foo: "" }) // error under excess properties:  Object literal may only specify known properties, and 'foo' does not exist in type
foo({ foo: ""} as { foo: "" }) // error under weak types: Type '{ foo: ""; }' has no properties in common with type '{ bar?: number; }'.

ฉันคิดว่านี่คือช่องโหว่ (ฉันลังเลที่จะบอกว่าเป็นบั๊ก ไม่แน่ใจว่ามันถูกออกแบบมาแบบนี้หรือไม่) ในการตรวจสอบประเภทที่อ่อนแอ ประเภทที่อ่อนแอคือ (ตาม PR นี้):

  1. ประเภทวัตถุที่มีคุณสมบัติอย่างน้อยหนึ่งอย่าง
  2. ที่คุณสมบัติทั้งหมดเป็นทางเลือก
  3. และซึ่งไม่มีลายเซ็นดัชนีสตริง ลายเซ็นดัชนีตัวเลข ลายเซ็นการเรียก หรือ ลายเซ็นการสร้าง

อย่างไรก็ตาม ในการนำไปใช้ของการตรวจสอบประเภทอ่อนสำหรับการตัดกัน ประเภททั้งหมดของการตัดกันต้องเป็นประเภทอ่อนเพื่อให้การตัดกันเป็นประเภทอ่อน จากโค้ดของคอมไพเลอร์ (เพิ่มคอมเมนต์แล้ว):

function isWeakType(type: Type): boolean {
    if (type.flags & TypeFlags.Object) {
       // ....
    }
    if (type.flags & TypeFlags.Intersection) {
        /// All intersection members have to be weak
        return every((<IntersectionType>type).types, isWeakType); 
    }
    return false;
}

เนื่องจาก interface Bar {} ไม่ใช่ประเภทอ่อน (ตามกฎข้อแรก มันไม่มีสมบัติใดๆ) การตัดกันใดๆ กับมันจะไม่ใช่ประเภทอ่อนและจะไม่เกิดข้อผิดพลาดในการตรวจสอบประเภทอ่อน การลบ Bar ออกจากการตัดกันจะทำให้เกิดข้อผิดพลาดทุกที่ที่คุณกำหนดวัตถุที่ไม่มีอะไรเหมือนกันกับเป้าหมาย