I have defined an interface like this:
interface IModal {
content: string;
form: string;
href: string;
$form: JQuery;
$message: JQuery;
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();
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 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 :)
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
}
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';
})