跳到主要内容

TypeScript 高级类型

交叉类型

交叉类型是将多个类型合并为一个类型,使用&运算符定义。例如:

type PartialPointX = { x: number };
type Point = PartialPointX & { y: number };

let point: Point = {
x: 1,
y: 1,
};

上面的例子中,我们先定义了PartialPointX类型,它有一个x属性。然后使用&运算符创建一个新的Point类型,它包含了PartialPointX{ y: number }的所有属性。这样point对象就同时拥有了xy属性。

交叉类型常用于混入(mixin)场景,可以方便地实现对象组合。

联合类型

联合类型表示取值可以为多种类型中的一种,使用|运算符定义。当我们希望属性值可以是多种类型之一时,联合类型非常有用。例如:

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven'; // OK
myFavoriteNumber = 7; // OK

上面的例子中,myFavoriteNumber的类型是string | number,表示它可以是字符串或者数字类型。

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:

function getLength(something: string | number): number {
return something.length; // Error
}

上面的例子会报错,因为length不是stringnumber的共有属性。要解决这个问题,需要使用类型断言:

function getLength(something: string | number): number {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}

类型保护

联合类型适合于那些值可以为不同类型的情况。 但当我们想确切地了解是否为string时,比如上面的例子,怎么办?

JavaScript 里常用来区分 2 个可能值的方法是检查成员是否存在。比如上面的例子,我们就可以通过判断something.length是否存在来判断它是否是字符串:

function getLength(something: string | number): number {
if (something.length) {
return something.length;
} else {
return something.toString().length;
}
}

这种判断方式在 TypeScript 里被称为类型保护(type guard)。除了typeofinstanceof运算符外,TypeScript 还提供了一些特殊的语法来实现类型保护。

typeof 类型保护

typeof类型保护只支持两种形式:

  • typeof v === "typename"
  • typeof v !== "typename"

其中"typename"必须是"number""string""boolean""symbol"。 但是 TypeScript 并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。例如:

function padLeft(value: string, padding: string | number) {
if (typeof padding === 'number') {
return Array(padding + 1).join(' ') + value;
}
if (typeof padding === 'string') {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}

typeof类型保护可以让我们在判断过类型后,在分支里正确地使用对应类型的属性和方法。

instanceof 类型保护

instanceof类型保护是通过构造函数来细化类型的一种方式。比如:

class Bird {
fly() {
console.log("I'm flying!");
}
}

class Fish {
swim() {
console.log("I'm swimming!");
}
}

function move(animal: Bird | Fish) {
if (animal instanceof Bird) {
animal.fly();
} else if (animal instanceof Fish) {
animal.swim();
}
}

上面的例子中,animal可能是BirdFish类型。我们可以使用instanceof来判断它是哪个类型的实例,从而调用正确的方法。

in 类型保护

in操作符可以安全地检查一个对象上是否存在一个属性,它通常也被作为类型保护使用:

interface A {
x: number;
}

interface B {
y: string;
}

function doStuff(q: A | B) {
if ('x' in q) {
// q: A
} else {
// q: B
}
}

上面的例子中,'x' in q可以判断q是否为A类型。在if分支中,TypeScript 会将q的类型收窄为A,这样我们就可以放心地使用它的x属性了。

可辨识联合类型

interface Square {
kind: "square";
size: number;
}

interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}

type Shape = Square | Rectangle;

function area(s: Shape) {
switch (s.kind) {
case "square": return s.size _ s.size;
case "rectangle": return s.width _ s.height;
}
}