This blog mainly discusses the key differences of interface vs type
, and share how I would use them in my daily software development routine.
Describe the shape of an object or a function signature
Both can be used to describe the shape of an object or a function signature.
However, I mostly use interface
to describe the object, and use type
to describe a function signature, as I can clearly know it’s a definition for function from arrow function
, which is impossible to realize in interface
.
interface Point {
x: number;
y: number;
}
type SetPoint = (x: number, y: number) => void;
Another scenario where I use interface is for 「Index Signatures」. For example, in dynamic generated input, we don’t know all the names of a type’s properties ahead of time, but we do know the shape of the values.
interface Input {
[index: string]: string | number;
}
Extending and Intersection Types
At first sight, extending and intersection are similar ways to combine types. However, the principle difference between the two is how conflicts are handled.
For interface
, If you declare a property with the same key, it will overrides the one in the derived interface. However, it should be compatible with the previous one, or typescript compiler will throw an error.
// This works pretty well, as it is compatible
interface Converter {
convert: (value: number) => string;
}
interface StringNumberConverter extends Converter {
convert: (value: number | string) => string;
}
// Oops...this will throw an error, as it is incompatible
interface Converter {
convert: (value: number) => string;
}
interface StringNumberConverter extends Converter {
convert: (value: string) => string;
}
For type, properties with the same key don’t have to be compatible. You can declare whatever you like.
type Converter = {
convert: (value: number) => string;
}
type StringNumberConverter = Converter & {
convert: (value: string) => string;
}
Another difference you should pay attention is the meaning they represent. When I use interface, I want it stands for the「Parent-Child」relationship. By contrast, when I use type, it only stands for 「Composition」relationship.
// 「Parent-Child」relationship. Cat derives from Animal.
interface Animal {
name: string;
}
interface Cat extends Animal {
color: string;
}
// 「Composition」relationship. Color and Size are two seperate species
type Color = {
name: string
}
type Size = {
size: 'small' | 'medium' | 'large'
}
type ColorSize = Color & Size
Class Implements
A class can implement an interface or type alias, but it can not implement Union type, as in typescript, a class can only implement an object type or intersection of object types with statically known members.
So in daily development, if this type is designed to be implemented by class, I will use interface
over type
.
type PartialPoint = { x: number; } | { y: number; };
// can not implement a union type
class SomePartialPoint implements PartialPoint {
x = 1;
y = 2;
}
More than Object Types
Unlike an interface, type alias can also be used for other types. Frequently used are such as union type and tuples.
type StringNumberPair = [string, number];
function doSomething(stringHash: [string, number]) {
const [inputString, hash] = stringHash;
console.log(inputString); // string
console.log(hash); // number
}
Declaration Merging
interface
is open, and type alias
is close within itself. An interface can be defined multiple times, and will be treated as a single interface with members of all declarations being merged.
type Bear = {
name: string;
}
// Error:Duplicate Identifier
type Bear = {
age: number;
}
// works pretty well
interface Bear {
name: string;
}
interface Bear {
age: number;
}
const b: Bear = {
name: '',
age: 0
}
Working With typeof
Type Operator
In most cases, if you want to work with typeof
type operator, choose type
over interface
.
function f() {
return { x: 10, y: 3 };
}
type P = ReturnType<typeof f>;
/**
** type P = {
x: number;
y: number;
}
**/
Conclusion
When the next time you are confused which to use, try this cheatsheet!
Top comments (0)