ส่งวัตถุไปยังส่วนต่อประสานใน TypeScript


95

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

นี่คืออินเทอร์เฟซของฉัน:

export interface IToDoDto {
  description: string;
  status: boolean;
};

นี่คือรหัสที่ฉันพยายามสร้างนักแสดง:

@Post()
addToDo(@Response() res, @Request() req) {
  const toDo: IToDoDto = <IToDoDto> req.body; // <<< cast here
  this.toDoService.addToDo(toDo);
  return res.status(HttpStatus.CREATED).end();
}

และสุดท้ายวิธีการบริการที่เรียกว่า:

public addToDo(toDo: IToDoDto): void {
  toDo.id = this.idCounter;
  this.todos.push(toDo);
  this.idCounter++;
}

ฉันสามารถส่งผ่านข้อโต้แย้งใด ๆได้แม้แต่ข้อโต้แย้งที่ไม่เข้าใกล้คำจำกัดความของอินเทอร์เฟซและรหัสนี้จะทำงานได้ดี ฉันคาดหวังว่าหากไม่สามารถส่งการส่งจากเนื้อหาการตอบสนองไปยังอินเทอร์เฟซได้ข้อยกเว้นจะถูกส่งไปที่รันไทม์เช่น Java หรือ C #

ฉันได้อ่านแล้วว่าใน TypeScript ไม่มีอยู่มีเพียง Type Assertion ดังนั้นมันจะบอกคอมไพเลอร์ว่าวัตถุเป็นประเภทxเท่านั้นดังนั้น ... ฉันผิดหรือเปล่า? วิธีใดในการบังคับใช้และรับรองความปลอดภัยประเภทที่ถูกต้อง


1
โปรดกำหนด "มันใช้งานไม่ได้" มีความแม่นยำ มีข้อผิดพลาดหรือไม่? อันไหน? ในเวลาคอมไพล์? ที่รันไทม์? เกิดอะไรขึ้น?
JB Nizet

1
ในรันไทม์รหัสจะทำงานตามปกติโดยมีวัตถุอะไรก็ตามที่ฉันผ่าน
Elias Garcia

ไม่ชัดเจนว่าคุณกำลังถามอะไร
Nitzan Tomer

คำถามของฉันคือวิธีโยนวัตถุที่เข้ามาไปยังวัตถุที่พิมพ์ หากไม่สามารถแคสต์ได้ให้ยกเว้นรันไทม์เช่น Java, C # ...
Elias Garcia

สิ่งนี้ตอบคำถามของคุณหรือไม่? TypeScript หรือ JavaScript type casting
Michael Freidgeim

คำตอบ:


136

ไม่มีการแคสต์ในจาวาสคริปต์ดังนั้นคุณจึงไม่สามารถโยนได้หาก "แคสต์ล้มเหลว"
typescript รองรับการแคสต์แต่มีไว้สำหรับเวลารวบรวมเท่านั้นและคุณสามารถทำได้ดังนี้:

const toDo = <IToDoDto> req.body;
// or
const toDo = req.body as IToDoDto;

คุณสามารถตรวจสอบที่รันไทม์ว่าค่าถูกต้องหรือไม่และหากไม่เกิดข้อผิดพลาดเช่น:

function isToDoDto(obj: any): obj is IToDoDto {
    return typeof obj.description === "string" && typeof obj.status === "boolean";
}

@Post()
addToDo(@Response() res, @Request() req) {
    if (!isToDoDto(req.body)) {
        throw new Error("invalid request");
    }

    const toDo = req.body as IToDoDto;
    this.toDoService.addToDo(toDo);
    return res.status(HttpStatus.CREATED).end();
}

แก้ไข

ดังที่ @huyz ชี้ให้เห็นว่าไม่จำเป็นต้องมีการยืนยันประเภทเนื่องจากisToDoDtoเป็นตัวป้องกันประเภทดังนั้นสิ่งนี้ควรเพียงพอ:

if (!isToDoDto(req.body)) {
    throw new Error("invalid request");
}

this.toDoService.addToDo(req.body);

ฉันไม่คิดว่าคุณต้องการนักแสดงconst toDo = req.body as IToDoDto;เนื่องจากคอมไพเลอร์ TS รู้ว่ามันเป็นIToDoDtoจุดนี้
huyz

9
สำหรับใครที่กำลังมองหาประเภทการยืนยันโดยทั่วไปอย่าใช้ <> เลิกใช้แล้ว ใช้as
Abhishek Deb

" ไม่มีการแคสต์ในจาวาสคริปต์ดังนั้นคุณจึงไม่สามารถโยนได้หาก" การแคสต์ล้มเหลว " " ฉันคิดว่ายิ่งไปกว่านั้นอินเทอร์เฟซใน TypeScript ไม่สามารถดำเนินการได้ ในความเป็นจริงพวกเขากำลัง 100% น้ำตาล syntatic ทำให้ง่ายต่อการรักษาโครงสร้างตามแนวความคิดแต่ไม่มีผลกระทบที่แท้จริงต่อรหัสที่เกิดขึ้นซึ่งก็คือ imo สร้างความสับสน / ต่อต้านรูปแบบอย่างบ้าคลั่งเนื่องจากหลักฐานคำถามของ OP ไม่มีเหตุผลใดที่ไม่ตรงกับอินเทอร์เฟซที่ไม่สามารถส่งผ่าน JavaScript ที่แยกได้ เป็นทางเลือกที่ใส่ใจ (และไม่ดี imo) โดย TypeScript
ruffin

อินเทอร์เฟซ @ruffin ไม่ใช่น้ำตาลที่เป็นประโยค แต่พวกเขาได้ทำการเลือกอย่างมีสติเพื่อให้มันอยู่ในรันไทม์เท่านั้น ฉันคิดว่ามันเป็นทางเลือกที่ดีวิธีนี้จะไม่มีการลงโทษประสิทธิภาพที่รันไทม์
Nitzan Tomer

Tomayto tomahto ? ประเภทความปลอดภัยจากอินเทอร์เฟซใน TypeScript ไม่ได้ขยายไปถึงโค้ดที่ใช้งานได้ของคุณและแม้แต่การรันไทม์ก่อนรันไทม์ความปลอดภัยของประเภทก็มีข้อ จำกัดอย่างมากดังที่เราเห็นในปัญหาของ OP ที่ไม่มีความปลอดภัยเลย TS สามารถพูดว่า "เดี๋ยวก่อนรอanyไม่รับประกันว่าคุณจะIToDoDtoยังไม่ไป!" แต่ TS เลือกที่จะไม่ หากคอมไพลเลอร์จับเฉพาะความขัดแย้งบางประเภทและไม่มีในโค้ดที่แยกออกมา (และคุณพูดถูกฉันควรจะชัดเจนมากขึ้น @ ในต้นฉบับ) อินเทอร์เฟซน่าเสียดาย imo, [ส่วนใหญ่?] น้ำตาล
ruffin

7

นี่เป็นอีกวิธีหนึ่งในการบังคับให้ type-cast แม้จะอยู่ระหว่างประเภทและอินเทอร์เฟซที่เข้ากันไม่ได้ซึ่งโดยปกติคอมไพเลอร์ TS จะบ่น:

export function forceCast<T>(input: any): T {

  // ... do runtime checks here

  // @ts-ignore <-- forces TS compiler to compile this as-is
  return input;
}

จากนั้นคุณสามารถใช้มันเพื่อบังคับให้โยนวัตถุบางประเภท:

import { forceCast } from './forceCast';

const randomObject: any = {};
const typedObject = forceCast<IToDoDto>(randomObject);

โปรดทราบว่าฉันได้ละเว้นส่วนที่คุณควรจะต้องทำการตรวจสอบรันไทม์ก่อนที่จะส่งเพื่อลดความซับซ้อน สิ่งที่ฉันทำในโครงการของฉันคือการรวบรวม.d.tsไฟล์อินเทอร์เฟซทั้งหมดของฉันลงในสคีมา JSON และใช้ajvเพื่อตรวจสอบความถูกต้องในรันไทม์


1

ถ้ามันช่วยใครได้ฉันกำลังมีปัญหาที่ฉันต้องการจัดการกับวัตถุเป็นประเภทอื่นที่มีอินเทอร์เฟซที่คล้ายกัน ฉันพยายามทำสิ่งต่อไปนี้:

ไม่ผ่านการขุย

const x = new Obj(a as b);

linter ถูกบ่นว่าคุณสมบัติที่ขาดหายไปที่มีอยู่ในa bกล่าวอีกนัยหนึ่งaมีคุณสมบัติและวิธีการบางอย่างbแต่ไม่ใช่ทั้งหมด เพื่อหลีกเลี่ยงสิ่งนี้ฉันทำตามคำแนะนำของ VS Code:

ผ่านการทดสอบและขุย

const x = new Obj(a as unknown as b);

โปรดทราบว่าหากรหัสของคุณพยายามเรียกคุณสมบัติอย่างใดอย่างหนึ่งที่มีอยู่ในประเภทbที่ไม่ได้ใช้กับประเภทaคุณควรตระหนักถึงความผิดพลาดของรันไทม์


1
ฉันดีใจที่พบคำตอบนี้ แต่โปรดทราบว่าหากคุณส่ง 'x' ผ่านเครือข่ายหรือไปยังแอปอื่นคุณอาจรั่วไหลข้อมูลส่วนบุคคล (ถ้า 'a' เป็นผู้ใช้เป็นต้น) เนื่องจาก 'x' ยังคง มีคุณสมบัติทั้งหมดของ 'a' ซึ่งไม่สามารถใช้ได้กับ typescript
ZoltánMatók

@ ZoltánMatókจุดดี. นอกจากนี้เกี่ยวกับการส่งวัตถุที่ทำให้เป็นอนุกรมผ่านเครือข่ายมีอาร์กิวเมนต์สำหรับตัวรับและตัวตั้งค่าสไตล์ Java บน JavaScript getและsetวิธีการ
Jason
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.