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 นี้):
- ประเภทวัตถุที่มีคุณสมบัติอย่างน้อยหนึ่งอย่าง
- ที่คุณสมบัติทั้งหมดเป็นทางเลือก
- และซึ่งไม่มีลายเซ็นดัชนีสตริง ลายเซ็นดัชนีตัวเลข ลายเซ็นการเรียก หรือ ลายเซ็นการสร้าง
อย่างไรก็ตาม ในการนำไปใช้ของการตรวจสอบประเภทอ่อนสำหรับการตัดกัน ประเภททั้งหมดของการตัดกันต้องเป็นประเภทอ่อนเพื่อให้การตัดกันเป็นประเภทอ่อน จากโค้ดของคอมไพเลอร์ (เพิ่มคอมเมนต์แล้ว):
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 ออกจากการตัดกันจะทำให้เกิดข้อผิดพลาดทุกที่ที่คุณกำหนดวัตถุที่ไม่มีอะไรเหมือนกันกับเป้าหมาย
