Interface and Type Alias allows us to create custom types in TypeScript. In this tutorial let us learn the similarities and differences between Interface Vs Type Alias.
Table of Contents
Syntax
The syntax for both are similar, except in type alias we use the assignment operator
The syntax for creating an interface starts with the keyword interface
followed by name of the interface (IProduct
). Then followed by the shape of the object in curly brackets ({ }
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | interface IProduct { name:string, price:number, calculate:(qty:number)=> number } //Creating an object using Interface let i:IProduct = { name:"Samsung Galaxy", price:100, calculate(qty:number): number { return this.price*qty; } } console.log(i) //{ "name": "Samsung Galaxy", "price": 100 } console.log(i.calculate(10)) //1000 |
The syntax for creating a Type Alias starts with the keyword Type
followed by name of the type (TProduct
). Then followed by an assignment operator and then by a type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | type TProduct = { name:string, price:number, calculate:(qty:number)=> number } //Creating an object using Type Alias let t:TProduct = { name:"Samsung Galaxy", price:100, calculate(qty:number): number { return this.price*qty; } } console.log(t) //{ "name": "Samsung Galaxy", "price": 100 } console.log(t.calculate(10)) //1000 |
Type alias does not create a new type
Type alias does not create a new type. it just gives a new name to an existing type. The Interface creates a new type. This is a very important distinction between a Type Alias and an interface.
The type on the right of the assignment operator can be any type. It can be an existing type or a new type. The type keyword just gives it a name.
In this example, we create a new object type. We then use the type alias to give it a name TProduct
1 2 3 4 5 6 7 | //Create a new type and give it a name type TProduct = { name:string, } |
In this example, we create a new type alias (TProduct1) for an already existing type i.e. TProduct.
1 2 3 4 5 6 7 8 9 10 11 12 13 | //give a new name to an existing type type TProduct1 = TProduct //Creating an object using Type Alias let t:TProduct1 = { name:"Samsung Galaxy", } console.log(t) //{"name": "Samsung Galaxy"} |
While Interface always creates a new type. You cannot use it on an existing type.
In this example IProduct
is a new object type with a property name.
1 2 3 4 5 | interface IProduct { name:string, } |
Primitives
We can assign a name to any type using a type alias. For example, we can assign a new name to primitive types.
In this example stringType
is another name for the type string
.
1 2 3 4 5 6 7 8 | type stringType = string let a:stringType a="Hello" //Ok a=10 //Type 'number' is not assignable to type 'string'. |
We cannot use an interface to create a primitive type.
Union Types
Type alias can give a name to a union type. In this example stringOrNumber
is a name for the union type of string | number
1 2 3 | type stringOrNumber = string | number |
We cannot do that using the interface.
You can also create union types of complex types like objects or interfaces etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | interface Bird { fly(): void; layEggs(): void; } interface Fish { swim(): void; layEggs(): void; } type TPet = Bird | Fish let t:TPet = { fly() {}, swim() {}, layEggs() {} } |
We can implement this by creating a new interface by extending interfaces (or type alias). This works as long as the common properties and methods have compatible signatures. If they differ then it is not possible to extend them
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | interface Bird { fly(): void; layEggs(): void; } interface Fish { swim(): void; layEggs(): void; } interface IPet extends Bird, Fish {} let i:IPet = { fly() {}, swim() {}, layEggs() {} } |
IntersectionTypes
Type alias can give a name to an Intersection Type.
1 2 3 4 5 6 7 8 9 10 11 12 13 | interface Person { name: string; age: number; } interface Student { studentCode: string; division: string } type s = Person & Student |
But we cannot do that with an interface.
We can create a new interface by extending from the existing interfaces. It will work very similarly to how the intersection types work. The difference is how they handle the common properties. You can read more about them from intersection Types & Extending interfaces.
Declaration Merging
Declaration merging means that the compiler merges two or more separate declarations declared with the same name into a single definition.
Interfaces with the same name are merged.
For example the two IEmployee
interface in the following code does not raise any errors
1 2 3 4 5 6 7 8 9 10 11 12 13 | interface IEmployee { firstName: string; lastName: string; fullName(): string; } interface IEmployee { address: string; city:string state:string } |
They are merged into a single IEmployee
interface
1 2 3 4 5 6 7 8 9 10 11 | //merged to interface IEmployee { firstName: string; lastName: string; fullName(): string; address: string; city:string state:string } |
The two types with the same name are not allowed. The following code results in an error
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | type TEmployee = { firstName: string; lastName: string; fullName(): string; } type TEmployee ={ address: string; city:string state:string } //Duplicate identifier 'TEmployee'. |
Extending Types
An interface can extend another interface, class, or Type alias. It can extend a type alias provided it is an object type or intersection of object types with statically known members. The type alias cannot be extended if it is a primitive type or a union type.
A Type alias cannot extend an Interface, class, or Type alias.
In this example, the Employee interface extends from Address
and Employment
interface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | interface Address { address: string; city:string state:string } interface Employment { designation: string; } interface Employee extends Address, Employment { firstName: string; lastName: string; fullName(): string; } |
In Type Alias, you can use the Intersection Types instead. But there are a few differences regarding how they handle the common properties. You can read more about them from intersection Types & Extending interfaces.
Class can implement both Type and Interface
A TypeScript class can implement an Interface. It can also implement a type provided it is an object type or intersection of object types with statically known members
This example shows how we implement a class using both type and interface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | interface Person { name: string; } type Employee = { name: string; } class p implements Person { constructor(public name:string) { this.name=name } } class e implements Employee { constructor(public name:string) { this.name=name } } |
A class cannot implement a type alias of any primitive type or any union types
Functions
Both Type Alias and an interface can be used to describe a function.
The following is the example of how to describe an add
function using both interface and type alias.
1 2 3 4 5 6 7 | interface IAdd { (arg1:number,arg2:number):number } type TAdd = (arg1:number,arg2:number) => number |
Tuples
Tuples are easily described by using Type Alias.
1 2 3 4 | type TData = [string, number, number]; let t:TData=["Hello",0,0] |
We can also use an interface to define a Tuple
1 2 3 4 5 6 7 8 | type Type<T> = T; interface IData extends Type<[string, number, number]> { } let i:IData=["Hello",0,0] |