I have defined an interface like this:
interface IModal {
content: string;
form: string;
href: string;
$form: JQuery;
$message: JQuery;
$modal: JQuery;
$submits: JQuery;
}
I define a variable like this:
var modal: IModal;
However, when I try to set the property of modal it gives me a message saying that
"cannot set property content of undefined"
Is it okay to use an interface to describe my modal object and if so how should I create it?
If you are creating the "modal" variable elsewhere, and want to tell TypeScript it will all be done, you would use:
declare const modal: IModal;
If you want to create a variable that will actually be an instance of IModal in TypeScript you will need to define it fully.
const modal: IModal = {
content: '',
form: '',
href: '',
$form: null,
$message: null,
$modal: null,
$submits: null
};
Or lie, with a type assertion, but you'll lost type safety as you will now get undefined in unexpected places, and possibly runtime errors, when accessing modal.content
and so on (properties that the contract says will be there).
const modal = {} as IModal;
Example Class
class Modal implements IModal {
content: string;
form: string;
href: string;
$form: JQuery;
$message: JQuery;
$modal: JQuery;
$submits: JQuery;
}
const modal = new Modal();
You may think "hey that's really a duplication of the interface" - and you are correct. If the Modal class is the only implementation of the IModal interface you may want to delete the interface altogether and use...
const modal: Modal = new Modal();
Rather than
const modal: IModal = new Modal();
If you want an empty object of an interface, you can do just:
var modal = <IModal>{};
The advantage of using interfaces in lieu of classes for structuring data is that if you don't have any methods on the class, it will show in compiled JS as an empty method. Example:
class TestClass {
a: number;
b: string;
c: boolean;
}
compiles into
var TestClass = (function () {
function TestClass() {
}
return TestClass;
})();
which carries no value. Interfaces, on the other hand, don't show up in JS at all while still providing the benefits of data structuring and type checking.
var modal= <abc>{}
we just have assigned modal of type abc of empty object but where we have declared type of modal ? i am using typescript6.
If you are using React, the parser will choke on the traditional cast syntax so an alternative was introduced for use in .tsx files
let a = {} as MyInterface;
https://www.typescriptlang.org/docs/handbook/jsx.html
You can do
var modal = {} as IModal
I think you have basically five different options to do so. Choosing among them could be easy depending on the goal you would like to achieve.
The best way in most of the cases to use a class and instantiate it, because you are using TypeScript to apply type checking.
interface IModal {
content: string;
form: string;
//...
//Extra
foo: (bar: string): void;
}
class Modal implements IModal {
content: string;
form: string;
foo(param: string): void {
}
}
Even if other methods are offering easier ways to create an object from an interface you should consider splitting your interface apart, if you are using your object for different matters, and it does not cause interface over-segregation:
interface IBehaviour {
//Extra
foo(param: string): void;
}
interface IModal extends IBehaviour{
content: string;
form: string;
//...
}
On the other hand, for example during unit testing your code (if you may not applying separation of concerns frequently), you may be able to accept the drawbacks for the sake of productivity. You may apply other methods to create mocks mostly for big third party *.d.ts interfaces. And it could be a pain to always implement full anonymous objects for every huge interface.
On this path your first option is to create an empty object:
var modal = <IModal>{};
Secondly to fully realise the compulsory part of your interface. It can be useful whether you are calling 3rd party JavaScript libraries, but I think you should create a class instead, like before:
var modal: IModal = {
content: '',
form: '',
//...
foo: (param: string): void => {
}
};
Thirdly you can create just a part of your interface and create an anonymous object, but this way you are responsible to fulfil the contract
var modal: IModal = <any>{
foo: (param: string): void => {
}
};
Summarising my answer even if interfaces are optional, because they are not transpiled into JavaScript code, TypeScript is there to provide a new level of abstraction, if used wisely and consistently. I think, just because you can dismiss them in most of the cases from your own code you shouldn't.
Here is another approach:
You can simply create an ESLint friendly object like this
const modal: IModal = {} as IModal;
Or a default instance based on the interface and with sensible defaults, if any
const defaultModal: IModal = {
content: "",
form: "",
href: "",
$form: {} as JQuery,
$message: {} as JQuery,
$modal: {} as JQuery,
$submits: {} as JQuery
};
Then variations of the default instance simply by overriding some properties
const confirmationModal: IModal = {
...defaultModal, // all properties/values from defaultModal
form: "confirmForm" // override form only
}
Since I haven't found an equal answer in the top and my answer is different. I do:
modal: IModal = <IModal>{}
There are 5 ways to use the interface.
interface IStudent {
Id: number;
name: string;
}
Method 1. all fields must assign data.
const obj1: IStudent = { Id: 1, name: 'Naveed' };
Method 2. my favorite one
const obj2 = { name: 'Naveed' } as IStudent ;
Method 3.
const obj3 = <IStudent >{name: 'Naveed'};
Method 4. use partial interface if all fields not required.
const obj4: Partial<IStudent > = { name: 'Naveed' };
Method 5. use ? Mark with interface fields if all fields not required.
const obj5: IStudent = { name: 'Naveed' };
Since the question includes usage of TypeScript, replacing
var modal: IModal;
by
let modal: IModal = {} as IModal;
should answer the question.
Many of the solutions so far posted use type assertions and therefor do not throw compilation errors if required interface properties are omitted in the implementation.
For those interested in some other robust, compact solutions:
Option 1: Instantiate an anonymous class which implements the interface:
new class implements MyInterface {
nameFirst = 'John';
nameFamily = 'Smith';
}();
Option 2: Create a utility function:
export function impl<I>(i: I) { return i; }
impl<MyInterface>({
nameFirst: 'John';
nameFamily: 'Smith';
})
You can set default values using Class
.
Without Class Constructor:
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}
With Class Constructor
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}
let deletedQuestionsDto = { examId: this.examId, questionId: q.id } as DeletedQuestionsDto;
Using your interface you can do
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.
}
}
You actually don't need the class to create the object. You can direct do this:
interface IModal {
content: string;
form: string;
}
onButtonSaveClick() {
let myModalClass: IModal = {
content: 'foo content',
form: 'foo form'
}
}
Here another solution what i am using frequently. However I am not sure is good practice or not, please comment below if not.
/// Interface
export default interface BookInterface {
title: string,
author: string,
id: any
}
/// Creating Class
export class BookClass implements BookInterface {
title: string;
author: string;
id: any;
constructor(title: string, author: string, id: any) {
this.title = title;
this.author = author;
this.id = id;
}
}
/// How to use it
let book: BookInterface = new BookClass(title, author, id);
Thanks :)
Success story sharing
var modal = <IModal>{};
this is what I was looking for ;-) thanks!{} as IModal
rather than<IModal>{}
. It removes an ambiguity in the language grammar when using<foo>
style assertions in JSX. Read more. And of course, uselet
orconst
instead ofvar
.