欢迎关注公众号
如果你用过TypeScript,一定接触过Interface
和Type
。
5秒钟思考一下,他俩有什么相同和不同?
如果你对他们的不同一无所知,那么请继续往下看。
如果我们声明一个Point
类型,可以通过以下两种方式都会达到我们想要的结果:
interface Point {
x: number;
y: number;
}
或者
type Point = {
x: number;
y: number;
}
上面的列子没有表现出 interface
和 type
之间的不同。
让我们进一步探索。
TypeScript的类型包括原始数据类型、对象类型、高级类型等。
原始数据类型包括:boolean
、number
、string
、null
、undefined
以及ES6中的新类型 Symbol
和ES10中的 BigInt
。
高级类型:联合类型(Unions Type),泛型(Generics)
联合类型(Unions Type)
联合类型(Union Types)表示取值可以为多种类型中的一种。
举个栗子:
function printId(id: number | string) {
console.log("Your ID is: " + id);
}
// OK
printId(101);
// OK
printId("202");
// Error
printId({ myID: 22342 });
//抛出错误信息
//Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'.
// Type '{ myID: number; }' is not assignable to type 'number'.
上面例子中如果删除传入一个对象 会抛出异常,不能把 '{ myID: number; }'
赋值给 string或者number类型。
function printId(id: number | string) {
console.log(id.toUpperCase());
}
//Error
Property 'toUpperCase' does not exist on type 'string | number'.
Property 'toUpperCase' does not exist on type 'number'.
我们来分析一下上面的报错,只有string
类型的字符串有toUpperCase
属性。number
类型没有toUpperCase
属性,ts类型推断的时候就会报错。感觉TypeScript很智能,可以提前感知错误。
如果想支持参数 既是 string类型,又是number类型,需要改进一下。如下:
function printId(id: number | string) {
if (typeof id === "string") {
// In this branch, id is of type 'string'
console.log(id.toUpperCase());
} else {
// Here, id is of type 'number'
console.log(id);
}
}
类型别名(Type Aliases)
类型别名是指给一个类型起个新名字。注意这里不是定义一个新的类型,而是给了一个新的名字。
类型别名常用于 原始类型,联合类型(Unions Type)
type Point = {
x: number;
y: number;
};
// Exactly the same as the earlier example
function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 100, y: 100 });
联合类型
type ID = number | string;
type UserInputSanitizedString = string;
function sanitizeInput(str: string): UserInputSanitizedString {
return "inputcontent"
}
类型别名是指给一个类型起个新名字,所以下面 string和UserInputSanitizedString都是相同类型
对象的类型—接口
在 TypeScript 中,我们使用接口(Interfaces)来定义对象类型。相比类型别名,Interfaces仅用于 对象类型。
继承—extend
interface
和 type
都支持继承,并且 interface
可以继承 type
,type
又可以继承interface
,只是语法不一样。举例说明:
1.interface
extend interface
interface PartialPointX { x: number; }
interface Point extends PartialPointX { y: number; }
2.interface
extend type
type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }
type
&type
type PartialPointX = { x: number; };
// 这里继承使用的是 &
type Point = PartialPointX & { y: number; };
type
&interface
interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };
实现—Implements
一个class可以实现(implement) interface 和 type,但不可以实现(implement)一个联合类型(unions type)
interface Point {
x: number;
y: number;
}
class SomePoint implements Point {
x = 1;
y = 2;
}
type AnotherPoint = {
x: number;
y: number;
};
class SomePoint2 implements AnotherPoint {
x = 1;
y = 2;
}
type PartialPoint = { x: number; } | { y: number; };
//不能实现一个联合类型,下面代码将会抛出异常
class SomePartialPoint implements PartialPoint {
x = 1;
y = 2;
}
声明合并—Declaration Merging
TypeScript在编译的时候,会将名字相同的interface
合并成一个接口,但是 type
就不行 。
看一个简单的例子,interface名字一样,但是 属性名不同
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 };
上面的例子 把两个Box的类型合并成了下面结果
interface Box {
height: number;
width: number;
scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 };
再来看一个例子,interface名字一样,但是 属性名也一样
interface Cloner {
clone(animal: Animal): Animal;
}
interface Cloner {
clone(animal: Sheep): Sheep;
}
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
三个 Cloner的interface 合并之后的结果如下:
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
仔细观察,合并之后,第三个接口的属性排在最前面,优先级最高。这个一定要注意
Interface
和Type
如何选择呢?
上面内容,我们比较了 Interface
和Type
两者之间的相同点和不同点。接下来总结一下。
对于库中类型或第三方库类型,定义公共 API ,应使用接口来提供声明合并功能。
除此之外,我们随便选择,但是要保障代码的一致性。