When to use Interface and Model in TypeScript / Angular
I recently watched a Tutorial on Angular 2 with TypeScript, but unsure when to use an Interface and when to use a Model for data structures.
Example of interface:
export interface IProduct {
ProductNumber: number;
ProductName: string;
ProductDescription: string;
}
Example of Model:
export class Product {
constructor(
public ProductNumber: number,
public ProductName: string,
public ProductDescription: string
){}
}
I want to load a JSON data from a URL and bind to the Interface/Model. Sometime I want a single data object, other time I want to hold an array of the object.
Which one should I use and why?
Solution 1:
Interfaces are only at compile time. This allows only you to check that the expected data received follows a particular structure. For this you can cast your content to this interface:
this.http.get('...')
.map(res => <Product[]>res.json());
See these questions:
- How do I cast a JSON object to a typescript class
- How to get Date object from json Response in typescript
You can do something similar with class but the main differences with class are that they are present at runtime (constructor function) and you can define methods in them with processing. But, in this case, you need to instantiate objects to be able to use them:
this.http.get('...')
.map(res => {
var data = res.json();
return data.map(d => {
return new Product(d.productNumber,
d.productName, d.productDescription);
});
});
Solution 2:
The Interface describes either a contract for a class or a new type. It is a pure Typescript element, so it doesn't affect Javascript.
A model, and namely a class, is an actual JS function which is being used to generate new objects.
I want to load JSON data from a URL and bind to the Interface/Model.
Go for a model, otherwise it will still be JSON in your Javascript.
Solution 3:
I personally use interfaces for my models, There hoewver are 3 schools regarding this question, and choosing one is most often based on your requirements:
1- Interfaces:
interface
is a virtual structure that only exists within the context of TypeScript. The TypeScript compiler uses interfaces solely for type-checking purposes. Once your code is transpiled to its target language, it will be stripped from its interfaces - JavaScript isn’t typed.
interface User {
id: number;
username: string;
}
// inheritance
interface UserDetails extends User {
birthdate: Date;
biography?: string; // use the '?' annotation to mark this property as optionnal
}
Mapping server response to an interface
is straight forward if you are using HttpClient
from HttpClientModule
if you are using Angular 4.3.x and above.
getUsers() :Observable<User[]> {
return this.http.get<User[]>(url); // no need for '.map((res: Response) => res.json())'
}
when to use interfaces:
- You only need the definition for the server data without introducing additional overhead for the final output.
- You only need to transmit data without any behaviors or logic (constructor initialization, methods)
- You do not instantiate/create objects from your interface very often
- Using simple object-literal notation
let instance: FooInterface = { ... };
, you risk having semi-instances all over the place. - That doesn't enforce the constraints given by a class ( constructor or initialization logic, validation, encapsulation of private fields...Etc)
- Using simple object-literal notation
- You need to define contracts/configurations for your systems (global configurations)
2- Classes:
A class
defines the blueprints of an object. They express the logic, methods, and properties these objects will inherit.
class User {
id: number;
username: string;
constructor(id :number, username: string) {
this.id = id;
this.username = username.replace(/^\s+|\s+$/g, ''); // trim whitespaces and new lines
}
}
// inheritance
class UserDetails extends User {
birthdate: Date;
biography?: string;
constructor(id :number, username: string, birthdate:Date, biography? :string ) {
super(id,username);
this.birthdate = ...;
}
}
when to use classes:
- You instantiate your class and change the instances state over time.
- Instances of your class will need methods to query or mutate its state
- When you want to associate behaviors with data more closely;
- You enforce constraints on the creation of your instaces.
- If you only write a bunch of properties assignments in your class, you might consider using a type instead.
2- Types:
With the latest versions of typescript, interfaces and types becoming more similar.
types
do not express logic or state inside your application. It is best to use types when you want to describe some form of information. They can describe varying shapes of data, ranging from simple constructs like strings, arrays, and objects.
Like interfaces, types are only virtual structures that don't transpile to any javascript, they just help the compiler making our life easier.
type User = {
id: number;
username: string;
}
// inheritance
type UserDetails = User & {
birthDate :Date;
biography?:string;
}
when to use types:
- pass it around as concise function parameters
- describe a class constructor parameters
- document small or medium objects coming in or out from an API.
- they don't carry state nor behavior