Transmisikan objek ke antarmuka di TypeScript

95

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?

Elias Garcia
sumber
1
Harap jelaskan "ini tidak berfungsi". Tepat. Apakah ada kesalahan? Yang mana? Saat kompilasi? Saat runtime? Apa yang terjadi?
JB Nizet
1
Saat runtime, kode dijalankan secara normal, dengan objek apa pun yang saya lewati.
Elias Garcia
Tidak jelas apa yang Anda tanyakan
Nitzan Tomer
Pertanyaan saya adalah bagaimana mentransmisikan objek yang masuk ke objek yang diketik. Jika pemeran tidak memungkinkan, lemparkan pengecualian saat runtime, seperti Java, C # ...
Elias Garcia
Apakah ini menjawab pertanyaan Anda? Pengecoran tipe TypeScript atau JavaScript
Michael Freidgeim

Jawaban:

134

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 isToDoDtomerupakan penjaga tipe, jadi ini sudah cukup:

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

this.toDoService.addToDo(req.body);
Nitzan Tomer
sumber
Saya tidak berpikir bahwa Anda memerlukan pemeran const toDo = req.body as IToDoDto;karena kompiler TS tahu bahwa ini adalah IToDoDtosaat ini
huyz
9
bagi siapa saja yang mencari pernyataan tipe secara umum, jangan gunakan <>. ini sudah usang. gunakanas
Abhishek Deb
" Tidak ada casting di javascript, jadi Anda tidak bisa melempar jika" casting gagal ". " Saya pikir, lebih tepatnya, antarmuka di TypeScript tidak dapat ditindaklanjuti; faktanya, mereka 100% gula sintetis . Mereka membuatnya lebih mudah untuk mempertahankan struktur secara konseptual , tetapi tidak memiliki dampak nyata pada kode yang ditranspilasi - yang, sangat membingungkan / anti-pola, seperti bukti pertanyaan OP. Tidak ada alasan hal-hal yang gagal untuk mencocokkan antarmuka tidak dapat memasukkan JavaScript yang transparan; itu adalah pilihan sadar (dan buruk, imo) oleh TypeScript.
ruffin
Antarmuka @ruffin bukan gula sintaksis, tetapi mereka membuat pilihan sadar untuk menyimpannya hanya dalam waktu proses. saya pikir itu pilihan yang bagus, dengan begitu tidak ada penalti kinerja saat runtime.
Nitzan Tomer
Tomayto tomahto ? Keamanan tipe dari antarmuka di TypeScript tidak mencakup kode transparan Anda, dan bahkan pra-runtime keamanan tipe sangat terbatas - seperti yang kita lihat dalam masalah OP di mana tidak ada keamanan tipe sama sekali . TS bisa berkata, "Hei, tunggu, kamu anybelum dijamin IToDoDto!", 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.
ruffin
7

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.tsfile antarmuka saya ke dalam skema JSON dan menggunakan ajvuntuk memvalidasi dalam runtime.

Sepehr
sumber
1

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 aproperti yang ada di dalamnya hilang b. Dengan kata lain, amemiliki beberapa sifat dan metode b, 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 byang tidak diimplementasikan pada tipe a, Anda harus menyadari kesalahan runtime.

Jason
sumber
1
Saya senang saya menemukan jawaban ini, tetapi perhatikan bahwa jika Anda mengirim 'x' melalui jaringan atau ke aplikasi lain, Anda bisa membocorkan info pribadi (jika 'a' adalah pengguna misalnya), karena 'x' masih memiliki semua properti 'a', mereka hanya tidak tersedia untuk skrip ketikan.
Zoltán Matók
@ Zoltánó poin yang bagus. Juga, terkait pengiriman objek berseri melalui jaringan, ada argumen untuk pengambil dan penyetel gaya Java melalui JavaScript getdan setmetode.
Jason