Saya mencoba membuat cast dalam kode saya dari tubuh permintaan secara ekspres (menggunakan middleware body-parser) ke antarmuka, tetapi tidak menegakkan keamanan tipe.
Ini adalah antarmuka saya:
export interface IToDoDto {
description: string;
status: boolean;
};
Ini adalah kode tempat saya mencoba melakukan pemeran:
@Post()
addToDo(@Response() res, @Request() req) {
const toDo: IToDoDto = <IToDoDto> req.body; // <<< cast here
this.toDoService.addToDo(toDo);
return res.status(HttpStatus.CREATED).end();
}
Dan terakhir, metode layanan yang dipanggil:
public addToDo(toDo: IToDoDto): void {
toDo.id = this.idCounter;
this.todos.push(toDo);
this.idCounter++;
}
Saya dapat menyampaikan argumen apa pun, bahkan argumen yang tidak cocok dengan definisi antarmuka , dan kode ini akan berfungsi dengan baik. Saya berharap, jika cast dari respon tubuh ke antarmuka tidak memungkinkan, pengecualian akan dilemparkan pada runtime seperti Java atau C #.
Saya telah membaca bahwa di TypeScript casting tidak ada, hanya Type Assertion, jadi itu hanya akan memberitahu compiler bahwa suatu objek bertipe x
, jadi ... Apakah saya salah? Apa cara yang tepat untuk menegakkan dan memastikan keamanan tipe?
sumber
Jawaban:
Tidak ada transmisi dalam javascript, jadi Anda tidak dapat membuang jika "transmisi gagal".
Ketikan mendukung casting tetapi itu hanya untuk waktu kompilasi, dan Anda dapat melakukannya seperti ini:
const toDo = <IToDoDto> req.body; // or const toDo = req.body as IToDoDto;
Anda dapat memeriksa pada saat runtime apakah nilainya valid dan jika tidak menimbulkan kesalahan, yaitu:
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(); }
Edit
Seperti yang ditunjukkan @huyz, tidak perlu pernyataan tipe karena
isToDoDto
merupakan penjaga tipe, jadi ini sudah cukup:if (!isToDoDto(req.body)) { throw new Error("invalid request"); } this.toDoService.addToDo(req.body);
sumber
const toDo = req.body as IToDoDto;
karena kompiler TS tahu bahwa ini adalahIToDoDto
saat inias
any
belum dijaminIToDoDto
!", Tapi TS memilih untuk tidak melakukannya. Jika kompiler hanya menangkap beberapa jenis konflik, dan tidak ada dalam kode transparan (dan Anda benar; saya seharusnya lebih jelas @ bahwa dalam aslinya), sayangnya, antarmuka, imo, [kebanyakan?] Gula.Berikut cara lain untuk memaksa tipe-cast bahkan antara tipe yang tidak kompatibel dan antarmuka yang biasanya dikeluhkan oleh compiler TS:
export function forceCast<T>(input: any): T { // ... do runtime checks here // @ts-ignore <-- forces TS compiler to compile this as-is return input; }
Kemudian Anda dapat menggunakannya untuk memaksa objek cor ke tipe tertentu:
import { forceCast } from './forceCast'; const randomObject: any = {}; const typedObject = forceCast<IToDoDto>(randomObject);
Perhatikan bahwa saya meninggalkan bagian yang seharusnya Anda lakukan pemeriksaan runtime sebelum casting demi mengurangi kompleksitas. Apa yang saya lakukan dalam proyek saya adalah mengumpulkan semua
.d.ts
file antarmuka saya ke dalam skema JSON dan menggunakanajv
untuk memvalidasi dalam runtime.sumber
Jika itu membantu siapa pun, saya mengalami masalah di mana saya ingin memperlakukan objek sebagai tipe lain dengan antarmuka yang serupa. Saya mencoba yang berikut:
Tidak lulus linting
const x = new Obj(a as b);
Linter mengeluh bahwa
a
properti yang ada di dalamnya hilangb
. Dengan kata lain,a
memiliki beberapa sifat dan metodeb
, tetapi tidak semua. Untuk mengatasi ini, saya mengikuti saran VS Code:Lulus linting dan pengujian
const x = new Obj(a as unknown as b);
Perhatikan bahwa jika kode Anda mencoba memanggil salah satu properti yang ada pada tipe
b
yang tidak diimplementasikan pada tipea
, Anda harus menyadari kesalahan runtime.sumber
get
danset
metode.