Working with Model Classes in ES6+ / TypeScript

I love using models in my code. I've written about my infatuation before. Clean ES6+ or TypeScript code just looks better than code littered with framework references and magic.

I primarily use them in my Aurelia code because I write of lot of code with Aurelia. My models typically have no reference to Aurelia at all so here on out I'm showing pure code to show off the simplicity.

ES6 Model Class

Models are representations of data.

They are not visual representations of the data, so when you find yourself adding presentation logic in the model you should take a step back.

Here's an example of a simple model class -

export class Character {  
  id = '';
  name = '';
  constructor(data = {}) {
      Object.assign(this, data);
    if (!this.id) {
      this.id = new Date().getTime();
    }
  }
}

You can see here we have a few default values given for our id and name properties. If the data that is passed in to the constructor has either of those properties defined with a value those string empty's will be overwritten by that value.

This is because we are using Object.assign() to conventionally map our data to our available properties.

Using our model class might look like this -

import {Character} from './models/character';  
export class MyViewModel {  
  character = new Character({name: 'Patrick'});
}

Keeping our models clean of procedural code yet fat with logic that can be highly re-used is essential. This helps to improve the testability of the code.

TypeScript Model Class

With TypeScript we can go one step further and let our consumers know about what can be set / read on the model as well as providing intellisense on our model instance makes it easier to share code bases without diverging on implementations.

We can use the same pattern of passing in a set of JSON data with TypeScript and use a union type. This gives us intellisense while allowing us to default back to the classes' default property values.

The same code above, in TypeScript -

export class Character {  
  id = '';
  name = '';
  constructor(data: Character | {} = {}) {
    Object.assign(this, data);
    if (!this.id) {
      this.id = new Date().getTime().toString();
    }
  }
}

Basically we have the exact same code but simply by adding the union type in the constructor to describe the 'data' object we get the added benefit of TypeScript letting everyone else know what we expect to be passed in.

Let me know what you think!