One of the things that you will do the most with Typescript is to define the shape of objects with type
or interface
. For that reason, understanding both well will make your TypeScript better quickly.
Let's see their main differences aside of the syntax, common patterns and surprising behaviours.
type
can be used for more things
While interface
is used just to define the shape of objects, type
has other use cases.
type Pet = 'Cat' | 'Dog'
type CoolNumbers = 3.1416 | 4 | 100
Interface merging
You should be aware of this one.
interface DesktopFile {
icon: string;
}
interface DesktopFile {
extension: string;
}
// Error: Property 'icon' is missing in type '{ extension: string; }'
// but required in type 'DesktopFile'.
const file: DesktopFile = {
extension: 'pdf',
}
It can be surprising that you can redeclare an interface and merge them!
This is also known as "interface augmenting" and can be desirable in some situations but is definitely unusual in other languages.
Note that using Type
would result in an error.
Discriminated union
Also known as "tagged union", is a frequent pattern in TypeScript.
It can be weird if you are used to polymorphism using classes, but since TypeScript's types disappear at runtime, you need to do things a bit different.
type File = {
kind: 'file';
name: string;
extension: string;
}
type Folder = {
kind: 'folder';
name: string;
filesInside: number;
}
type DesktopItem = File | Folder
const item: DesktopItem = {...}
if (item.kind === 'file'){
// TypeScript knows that the properties
// of the type File are defined here
}
This can be used like instanceof
in other languages.
Union of types vs types of unions
Generally prefer union of types.
type Vehicle = {
kind: 'motorcycle' | 'car'
numberOfWheels: number
numberOfAirbags: number | undefined
}
const vehicle: Vehicle = {...}
if (vehicle.kind === 'car'){
// TypeScript still thinks that
// numberOfAirbags could be undefined
}
If we used union of types instead, like in the "discriminated union" example, TypeScript can be sure that the Car
properties are available.
Excess property checking
This is a mechanism that can mess your mental model of structural typing when using type
and interface
.
interface Cat {
name: string;
whiskersLength: number;
}
const cat: Cat = {
name: 'Uxia',
whiskersLength: 6,
bestFriend: 'Nina',
// ~~~~~~~~~~~~~~~~~~ Object literal may only specify known properties,
// and 'bestFriend' does not exist in type 'Cat'
};
From a structural typing point of view it is valid as the defined object contains at least the properties declared for Cat
.
This is excess property checking complaining though.
Check out this case:
type Person = {
name: string;
zipCode?: string;
}
const randomGuy: Person = {
name: 'Pedro',
zip: '45420',
}
Excess property checking quickly points out an error that we could've spent too much time looking for otherwise.
Note that this check only happens when using object literals.
Should I use type
or interface
then?
I find type
easier to reason about and more readable.
One exception would be when extending types:
type Flyable = {
fly(): void;
}
type Airplane = Flyable & {
...
}
interface Helicopter extends Flyable {
...
}
Also, as we saw earlier, "interface augmenting" can be unfamiliar to many people.
Be aware of their differences, try to make your team agree in their uses for the sake of consistency and you will be fine.
Top comments (0)