How can I create an object based on an interface file definition in TypeScript?

Typescript

Typescript Problem Overview


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?

Typescript Solutions


Solution 1 - Typescript

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();

Solution 2 - Typescript

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.

Solution 3 - Typescript

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

Solution 4 - Typescript

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.

Solution 5 - Typescript

You can do

var modal = {} as IModal 

Solution 6 - Typescript

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
}

Solution 7 - Typescript

Since I haven't found an equal answer in the top and my answer is different. I do:

modal: IModal = <IModal>{}

Solution 8 - Typescript

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' };  

Solution 9 - Typescript

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';
})

Solution 10 - Typescript

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}

Solution 11 - Typescript

Since the question includes usage of TypeScript, replacing

var modal: IModal;

by

let modal: IModal = {} as IModal;

should answer the question.

Solution 12 - Typescript

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.
  }
}

Solution 13 - Typescript

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'
    }
}

Solution 14 - Typescript

let deletedQuestionsDto = { examId: this.examId, questionId: q.id } as DeletedQuestionsDto;

Solution 15 - Typescript

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 :)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1679941View Question on Stackoverflow
Solution 1 - TypescriptFentonView Answer on Stackoverflow
Solution 2 - Typescriptuser3180970View Answer on Stackoverflow
Solution 3 - TypescriptgegView Answer on Stackoverflow
Solution 4 - TypescriptUrsegorView Answer on Stackoverflow
Solution 5 - TypescriptKangudie Muanza - KillmongerView Answer on Stackoverflow
Solution 6 - TypescriptrbentoView Answer on Stackoverflow
Solution 7 - TypescriptZokorView Answer on Stackoverflow
Solution 8 - TypescriptNaveed UllahView Answer on Stackoverflow
Solution 9 - TypescriptStephen PaulView Answer on Stackoverflow
Solution 10 - TypescriptGorvGoylView Answer on Stackoverflow
Solution 11 - TypescriptPmagView Answer on Stackoverflow
Solution 12 - TypescriptByrdView Answer on Stackoverflow
Solution 13 - TypescriptFreddyView Answer on Stackoverflow
Solution 14 - TypescriptM KomaeiView Answer on Stackoverflow
Solution 15 - Typescriptdipenparmar12View Answer on Stackoverflow