区别
1. Objecs/Functions(对象和方法)
两个都可以用来定义对象或者函数签名。但是语法略有不同。
interface
interface Point {
x: number;
y: number;
}
interface SetPoint {
(x: number, y: number): void;
}
Type alias
type Point = {
x: number;
y: number;
};
type SetPoint = (x: number, y: number) => void;
2.Other Types
与接口不同,类型别名还可以用于其他类型,如基本类型、联合类型和元组。
// 基本类型 primitive
type Name = string;
// 对象 object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };
// 联合类型 union
type PartialPoint = PartialPointX | PartialPointY;
// 元组 tuple
type Data = [number, string];
3.继承
两者都可以通过继承来扩展,但是语法不同。另外,接口和类型别名不是互斥的。接口可以扩展类型别名,反之亦然。
interface extends interface
interface PartialPointX {
x: number;
}
interface Point extends PartialPointX {
y: number;
}
Type alias extends type alias
type PartialPointX = { x: number };
type Point = PartialPointX & { y: number };
Interface extends type alias
type PartialPointX = { x: number };
interface Point extends PartialPointX {
y: number;
}
Type alias extends interface
interface PartialPointX {
x: number;
}
type Point = PartialPointX & { y: number };
4.Implements
类可以以完全相同的方式实现接口或类型别名。但是请注意,类和接口被认为是静态模板。因此,它们不能实现/扩展以联合类型命名的类型别名。
interface Point {
x: number;
y: number;
}
class SomePoint implements Point {
x: number = 1;
y: number = 2;
}
type Point2 = {
x: number;
y: number;
};
class SomePoint2 implements Point2 {
x: number = 1;
y: number = 2;
}
type PartialPoint = { x: number } | { y: number };
// FIXME: 不能实现联合类型
class SomePartialPoint implements PartialPoint {
x = 1;
y = 2;
}
5.Declaration merging(声明合并)
与类型别名不同,接口可以被定义多次,并且将被视为单个接口(所有声明的成员都被合并)。
// 这两个声明会变成:
// interface Point { x: number; y: number; }
interface Point { x: number; }
interface Point { y: number; }
const point: Point = { x: 1, y: 2 };
使用方式
一般情况下:选择接口,少用类型别名(个人习惯)。
什么时候使用类型别名(type)?
- 为基本数据类型(字符串、布尔值、数字、
bigint、符号等)定义别名时使用type - 定义元组类型时使用
type - 定义函数类型时使用
type - 定义联合时使用
type - 当试图通过复合重载对象类型中的函数时,请使用
type - 当需要利用映射类型时使用
type
基本数据类型
只有type能为基本数据类型设置别名,interface是做不到的。
type Nullish = null | undefined;
type Fruit = 'apple' | 'pear' | 'orange';
type Num = number | bigint;
元组类型(Tuple types)
元组类型也只可以用type来定义:
type row = [colOne: number, colTwo: string];
函数类型
函数既可以用type定义,也可以用interface定义,个人感觉type比较方便一点,方便定义和阅读:
// 通过type定义
type Sum = (x: number, y: number) => number;
// 通过interface定义
interface Sum {
(x: number, y: number): number;
}
联合类型(union types)
联合类型只能用type定义:
type Fruit = 'apple' | 'pear' | 'orange';
type Vegetable = 'broccoli' | 'carrot' | 'lettuce';
type HealthyFoods = Fruit | Vegetable;
对象类型(Object types)
在为对象提供类型时,接口和类型都可以使用,什么时候使用interface什么时候使用type呢?
重载对象类型中的函数时,使用type
先来看一个例子:
interface NumLogger {
log: (val: number) => void;
}
// 或者这样写也是可以的
// type NumLogger = {
// log: (val: number) => void;
// };
type StrAndNumLogger = NumLogger & {
log: (vals: string) => void;
};
const logger: StrAndNumLogger = {
log: (val: string | number) => console.log(val),
};
logger.log("dasd");
logger.log(12);
对象中的log方法重载了,此时传入的参数可以是字符串也可以是数字
如果使用interface重载:
interface NumLogger {
log: (val: number) => void;
}
// 这里的StrAndNumLogger会报错
/**
* 接口'StrAndNumLogger'不正确地扩展接口'NumLogger'。
* 属性“log”的类型不兼容。
* Type '(val: string) => void'不能赋值给Type '(val: number) => void'。
* 参数'val'和'val'的类型不兼容。
* 类型“number”不能赋值给类型“string”。
*/
interface StrAndNumLogger extends NumLogger {
log: (val: string) => void;
};
const logger: StrAndNumLogger = {
log: (val: string | number) => console.log(val),
};
logger.log("dasd");
logger.log(12);
从报错信息可以看出,子可以重写父函数中的函数体,但是函数的类型是不能改变的。
什么时候使用接口(interface)?
- 对于不需要使用
type的所有对象类型使用interface - 当利用声明合并时,请使用
interface。