Everything you should know about types and interfaces in Typescript.

Everything you should know about types and interfaces in Typescript.

Typescript as you know is a better programming interface to Javascript as it a strongly typed language. For all practical purposes Typescript can be seen same as Javascript as it compiles to Javascript and can run everywhere Javascript can run. Hence "type" is a very important feature of javascript and probably the most loved feature that makes Typescript better than Javascript. In this article we will learn more about its usage and help you master it.

Table of contents

  1. What is a type ?

  2. Advantages of using Types in Typescript

  3. Three ways you can user Types in Typescript.

    1. Anonymous types

      1. When to use anonymous types
    2. Interfaces in Typescript

    3. type or type alias in Typescript

  4. Differences between type and interface in Typescript

    1. How to chose between type and interface ?
    2. A philosophical choice between Interfaces and Types
  5. any type

  6. Conclusion

What is a type ?

In ordinary javascript a variable can hold any value of differnt types. For example following code is a valid javascript code.


var myVar = 1;
myVar = "One";
myVar = [];
myVar = { name:"My Name" };

Here you can define a variable and assign any value to it. The type of the value is not decided when you define it first. This meaks Javascript a weakly typed language.

Weak typing makes the language very flexible. You dont have to worry about method signature, matching return types and so on. For quick and simple code this is very useful.

However, as the application grows bigger and team size grows this flexibility becomes an evil that is hard to get rid of. In weakly typed language bugs are easy to get introduced and hard to detect through tests or during compile time. It is even harder for your IDE to act smart offer code completion suggestions. Take for example following code.


function isThisTrue(){
return "false";
}

var flag = isThisTrue();
if(flag){
alert("Flag is true");
}

In above code the alert will show up because the "string" "false" evaluates to true. The coder who wrote the function isThisTrue() made a mistake and returned "false" when she meant to return false.

Detecting problems in run time is more expensive than detecting them in unit tests and detecting them in unit tests is more expensive that detecting them in your IDE itself.

This problem was address by Microsoft by compiling up with Typescript. It is nearly indistinguishable from modern Javascript except that is enforces concept of type.

Typescript has other features too but having Types by far is the most important feature as the name suggests.


function isThisTrue(): boolean{
return "false";
}

var flag = isThisTrue();
if(flag){
alert("Flag is true");
}

If you copy paste above code in a Typescript editor the editor itself will show you the error in your return type because the function expects a boolean return type. You can try it in this Typescipt editor.

I hope this should tell you what a type really means conceptually.

Advantages of using Types in Typescript

Imagine you go to a vending machine that is supposed to accept some currency and give you some item in return. The vending machine has a slit but it does not specify what kind of currency you should put in. You are worried that your $20 bill will go in but will get stuck. You are woried that it might not even take a currency note but expects coins.

This is exactly the problem with Javascript methods when there is no type specified.


function isValidUser(user) {
// 100 lines of code. ....

}

Can you read the above method and tell me what exactly I should pass to this method ? Is it some User object. Is is some userId ? Is it something else ? Also, how will this method tell you that user is valid or not ? It might return a boolean or it might return an object. You wont know unless you read the code.

When you have type, the API contract becomes lot cleaner, easy to read and easy to argue about correctness. There is no doubt as to what the method expects and what it is expected to return.

Three ways you can user Types in Typescript.

Typescript has three basic methods using which you can specify types. Generally people use all three of them depending on situation.

Anonymous types

These are very common when you write functions. As the name suggests you define them without giving them a name and since you do not give them a name you can not reuse them.


function greet(person: { name: string; age: number }) {
return "Hello " + person.name;
}

Here person is the argument name and the type of the argument is specified as { name: string; age: number }. This means the argument must have two properties. name and age.

What if you have an object that has many properties ?



const me = {
"name": "MyName",
"age": 18,
"location": "California"
};

// Will this work ?
greet(me);

function greet(person: { name: string; age: number }) {
return "Hello " + person.name;
}

Of course this will work. Because even though you object me has more properties, it has all the properties the method greet expects.

When to use anonymous types

  • Anonymous types must be used for smaller utility methods that do not need visibility for the entire object.
  • Anonymous types must be used where readability of the code is enhanced by their use and not hampered. For example an anonymous type with 2 fields is not just readable but also easy to understand for a programmer reading your code, but an anonymous type with 15 fields will make the code complex.

Interfaces in Typescript

If you are familiar with Java you know what interfaces are. The same concept applies in Typescript as well.


interface Person {
name: string;
age: number;
}

function greet(person: Person) {
return "Hello " + person.name;
}

Interfaces in typescript are templates or blueprints for objects. Typescript Interfaces have a very interesting property rarely seen in other languages. You can have multiple interfaces with the same name and Typescript merges them for you if needed automatically.


interface Person {
name: string
}

interface Person {
age: number
}

const john: Person = {
name: "John",
age: 26,
}

This sort of feature in typescript is very useful because often JSON Payloads an application deals with can be complex but different components might be interested only in a subset of it. However if you start definining different interfaces you will have very bloated code.


const someProductPayloag = {
name:"Product Name",
skus:[
{
id:"1"
}
],
inventory:{
available: 10
}

};

interface ProductWithOnlyName {
name: string;
}

interface ProductWithInventory {
inventory: {
available: number;
};
}

function renderAvailability(product: ProductWithInventory){

}

function renderProductName(product: ProductWithName){

}

// Note that we will need one more interface for this product here.
function renderProductPage(product){
renderAvailability(product);
renderProductName(product);
}

In above code we need three separate interfaces with three different names. But with this unique feature of interfaces all three interfaces can have same name and functions can use the same name.

type or type alias in Typescript

Other than interfaces Typescript provies another mechanism to enforce types. This is called type or type alias;


type Person = {
name: string;
age: number;
};

function greet(person: Person) {
return "Hello " + person.name;
}

As you can see type appears very similar to interface in terms of both syntax and functionality. But the differences are subtle. You can do nearly everything with type that you can do with interfaces and vice versa. In the next section we see how they differ.

Differences between type and interface in Typescript

The obvious differences is that they have different syntax. But they achieve the same end goal. Unlike interfaces types do not allow you to merge the two types with same name. Other than that you can just rely on any one of them for all your needs.

  1. Types can not be merged together
  2. Interfaces can be imposed in primitive types of strings.

Example:


type engineStatus = 'off' | 'idle' | 'on';

This can not be achieved using interface but very common pattern.

You can refer to this official documentation for differences in syntax of types and interfaces.

How to chose between type and interface ?

If they are so similar what should I consider when I am building my system ? I have seen this choice as mostly philosophical one. As long as your principle is applied consistently across codebase you can have any ground principle.

Some of the principles I have seen are like this.

  1. User interfaces when you want to enfoce an API contract on class. In short just like java.
  2. Use types for everything else.

Or

  1. Always use types and never use interfaces.

Or

  1. User interfaces and never use types.

or

  1. Use interfaces to define API contract.
  2. Use types for type checking and type enforcement inside the implementation.

or

  1. User type everywhere except for polymorphism.
  2. Use interfaces only whe polymorphism is needd.

A philosophical choice between Interfaces and Types

I think of interfaces as API contracts where as type is purely something that defines type of data. Hence, I use interfaces to define something which is combination of data and methods. Where as I use type to define pure data.

For example:



interface ProductSearchService{
query: string;
fetchProducts: (page:number, count:number) => Product[];
}

type Product = {
name: string;
link: string;
}


any type

Typescript provides a very generic inbuilt type any. As the name suggest it matches any type including undefined values. This should rarely be used but there are times it is useful.

Conclusion

Typescript provides three ways to enforce type. These are anonymous types, interfaces and type aliases. All three are widely used however there is some subtle differences and vast similarities between interfaces and type aliases which often confuse developers. This confusion is also fairly widespread and there are not right answers for it. Our recommendation is that instead of randomly picking interfaces or type prefer to use some logical reasoning and apply it across your codebase.