Bagaimana saya bisa membuat objek berdasarkan definisi file antarmuka di TypeScript?

313

Saya telah mendefinisikan antarmuka seperti ini:

interface IModal {
    content: string;
    form: string;
    href: string;
    $form: JQuery;
    $message: JQuery;
    $modal: JQuery;
    $submits: JQuery;
 }

Saya mendefinisikan variabel seperti ini:

var modal: IModal;

Namun, ketika saya mencoba untuk mengatur properti modal itu memberi saya pesan yang mengatakan itu

"cannot set property content of undefined"

Apakah saya boleh menggunakan antarmuka untuk menggambarkan objek modal saya dan jika demikian, bagaimana saya harus membuatnya?

Trilarion
sumber

Jawaban:

426

Jika Anda membuat variabel "modal" di tempat lain, dan ingin memberi tahu TypeScript semuanya akan dilakukan, Anda akan menggunakan:

declare const modal: IModal;

Jika Anda ingin membuat variabel yang sebenarnya akan menjadi instance dari IModal di TypeScript, Anda perlu mendefinisikannya sepenuhnya.

const modal: IModal = {
    content: '',
    form: '',
    href: '',
    $form: null,
    $message: null,
    $modal: null,
    $submits: null
};

Atau berbohong, dengan pernyataan jenis, tetapi Anda akan kehilangan keamanan jenis karena Anda sekarang akan tidak terdefinisi di tempat yang tidak terduga, dan mungkin kesalahan runtime, saat mengakses modal.contentdan sebagainya (properti yang menurut kontrak akan ada di sana).

const modal = {} as IModal;

Kelas Contoh

class Modal implements IModal {
    content: string;
    form: string;
    href: string;
    $form: JQuery;
    $message: JQuery;
    $modal: JQuery;
    $submits: JQuery;
}

const modal = new Modal();

Anda mungkin berpikir "hei itu benar-benar duplikasi antarmuka" - dan Anda benar. Jika kelas Modal adalah satu-satunya implementasi antarmuka IModal Anda mungkin ingin menghapus antarmuka sama sekali dan menggunakan ...

const modal: Modal = new Modal();

Daripada

const modal: IModal = new Modal();
Fenton
sumber
2
Terima kasih. Saya berharap untuk tidak harus mendefinisikan semua konten modal pada awalnya. Apakah akan lebih mudah jika daripada antarmuka saya mendefinisikan Modal sebagai kelas dengan properti dan kemudian menggunakan yang baru dan konstruktor untuk mengatur semua nilai awal?
1
Ya - Anda bisa mendefinisikan kelas dan kemudian Anda hanya perlu membuat instance baru saat Anda membutuhkannya. Apakah Anda memerlukan contoh?
Fenton
2
Saya telah menambahkan contoh dan catatan tentang antarmuka.
Fenton
21
var modal = <IModal>{};ini yang saya cari ;-) terima kasih!
Legenda
5
FYI, lebih baik menggunakan {} as IModaldaripada <IModal>{}. Ini menghapus ambiguitas dalam tata bahasa saat menggunakan <foo>pernyataan gaya di JSX. Baca lebih lanjut . Dan tentu saja, gunakan letatau constbukan var.
Alex Klaus
191

Jika Anda ingin objek kosong dari antarmuka, Anda bisa melakukannya:

var modal = <IModal>{};

Keuntungan menggunakan antarmuka sebagai pengganti kelas untuk menyusun data adalah bahwa jika Anda tidak memiliki metode apa pun di kelas, itu akan ditampilkan di JS yang dikompilasi sebagai metode kosong. Contoh:

class TestClass {
    a: number;
    b: string;
    c: boolean;
}

mengkompilasi menjadi

var TestClass = (function () {
    function TestClass() {
    }
    return TestClass;
})();

yang tidak memiliki nilai. Antarmuka, di sisi lain, tidak muncul di JS sama sekali sambil tetap memberikan manfaat penataan data dan pengecekan tipe.

pengguna3180970
sumber
1
`Untuk menambahkan komentar di atas. Untuk memanggil fungsi "updateModal (IModal modalInstance) {}", Anda dapat membuat instance inline dari antarmuka 'IModal', seperti ini: // beberapa kode di sini, di mana fungsi updateModal & IModal dapat diakses: updateModal (<IModal> {// baris ini membuat konten instan: '', form: '', href: '', $ form: null, $ message: null, $ modal: null, $ submits: null}); // lengkapi kode Anda `
Sunny
dengan menggunakan var modal= <abc>{}kita baru saja menetapkan modal jenis abc objek kosong tapi di mana kita telah menyatakan jenis modal? saya menggunakan typescript6.
Pardeep Jain
Ini tidak berhasil bagi saya, saya harus menginisialisasi seluruh objek seperti yang dinyatakan dalam jawaban yang diterima.
Rahul Sonwanshi
22

Saya pikir Anda pada dasarnya memiliki lima opsi berbeda untuk melakukannya. Memilih di antara mereka bisa mudah tergantung pada tujuan yang ingin Anda capai.

Cara terbaik di sebagian besar kasus untuk menggunakan kelas dan instantiate , karena Anda menggunakan TypeScript untuk menerapkan pengecekan tipe.

interface IModal {
    content: string;
    form: string;
    //...

    //Extra
    foo: (bar: string): void;
}

class Modal implements IModal {
    content: string;
    form: string;

    foo(param: string): void {
    }
}

Bahkan jika metode lain menawarkan cara yang lebih mudah untuk membuat objek dari antarmuka, Anda harus mempertimbangkan memisahkan antarmuka Anda , jika Anda menggunakan objek Anda untuk hal-hal yang berbeda, dan itu tidak menyebabkan pemisahan antarmuka yang berlebihan:

interface IBehaviour {
    //Extra
    foo(param: string): void;
}

interface IModal extends IBehaviour{
    content: string;
    form: string;
    //...
}

Di sisi lain, misalnya selama unit menguji kode Anda (jika Anda tidak sering menerapkan pemisahan masalah), Anda mungkin dapat menerima kekurangannya demi produktivitas. Anda dapat menerapkan metode lain untuk membuat tiruan sebagian besar untuk antarmuka pihak ketiga * .d.ts besar. Dan itu bisa menyebalkan untuk selalu mengimplementasikan objek anonim penuh untuk setiap antarmuka besar.

Di jalur ini, opsi pertama Anda adalah membuat objek kosong :

 var modal = <IModal>{};

Kedua untuk sepenuhnya mewujudkan bagian wajib antarmuka Anda . Ini bisa berguna apakah Anda memanggil perpustakaan JavaScript pihak ketiga, tetapi saya pikir Anda harus membuat kelas sebagai gantinya, seperti sebelumnya:

var modal: IModal = {
    content: '',
    form: '',
    //...

    foo: (param: string): void => {
    }
};

Ketiga, Anda dapat membuat hanya bagian dari antarmuka Anda dan membuat objek anonim , tetapi dengan cara ini Anda bertanggung jawab untuk memenuhi kontrak

var modal: IModal = <any>{
    foo: (param: string): void => {

    }
};

Meringkas jawaban saya walaupun antarmuka bersifat opsional, karena tidak diubah menjadi kode JavaScript, TypeScript hadir untuk memberikan tingkat abstraksi baru, jika digunakan secara bijak dan konsisten. Saya pikir, hanya karena Anda dapat mengabaikannya di sebagian besar kasus dari kode Anda sendiri, Anda seharusnya tidak melakukannya.

Ursegor
sumber
18

Anda dapat melakukan

var modal = {} as IModal 
Kangudie Muanza - Killmonger
sumber
12

Karena saya belum menemukan jawaban yang sama di atas dan jawaban saya berbeda. Saya lakukan:

modal: IModal = <IModal>{}
Zokor
sumber
2

Menggunakan antarmuka Anda bisa Anda lakukan

class Modal() {
  constructor(public iModal: IModal) {
   //You now have access to all your interface variables using this.iModal object,
   //you don't need to define the properties at all, constructor does it for you.
  }
}
Byrd
sumber
2

Berikut ini pendekatan lain:

Anda cukup membuat objek ramah ESLint seperti ini

const modal: IModal = {} as IModal;

Atau instance default berdasarkan antarmuka dan dengan default yang masuk akal, jika ada

const defaultModal: IModal = {
    content: "",
    form: "",
    href: "",
    $form: {} as JQuery,
    $message: {} as JQuery,
    $modal: {} as JQuery,
    $submits: {} as JQuery
};

Kemudian variasi instance default cukup dengan mengganti beberapa properti

const confirmationModal: IModal = {
    ...defaultModal,     // all properties/values from defaultModal
    form: "confirmForm"  // override form only
}
rbento
sumber
1

Banyak solusi yang diposting sejauh ini menggunakan pernyataan tipe dan karenanya tidak membuang kesalahan kompilasi jika properti antarmuka yang diperlukan dihilangkan dalam implementasi.

Bagi mereka yang tertarik pada beberapa solusi yang kuat dan kompak lainnya :

Opsi 1: Instantiate kelas anonim yang mengimplementasikan antarmuka:

new class implements MyInterface {
  nameFirst = 'John';
  nameFamily = 'Smith';
}();

Opsi 2: Buat fungsi utilitas:

export function impl<I>(i: I) { return i; }

impl<MyInterface>({
  nameFirst: 'John';
  nameFamily: 'Smith';
})
Stephen Paul
sumber
0

Anda dapat mengatur nilai default menggunakan Class.

Tanpa Konstruktor Kelas:

interface IModal {
  content: string;
  form: string;
  href: string;
  isPopup: boolean;
};

class Modal implements IModal {
  content = "";
  form = "";
  href: string;  // will not be added to object
  isPopup = true;
}

const myModal = new Modal();
console.log(myModal); // output: {content: "", form: "", isPopup: true}

Dengan Konstruktor Kelas

interface IModal {
  content: string;
  form: string;
  href: string;
  isPopup: boolean;
}

class Modal implements IModal {
  constructor() {
    this.content = "";
    this.form = "";
    this.isPopup = true;
  }

  content: string;

  form: string;

  href: string; // not part of constructor so will not be added to object

  isPopup: boolean;
}

const myModal = new Modal();
console.log(myModal); // output: {content: "", form: "", isPopup: true}
JerryGoyal
sumber