TypeScript 高级类型
交叉类型
交叉类型是将多个类型合并为一个类型,使用&
运算符定义。例如:
type PartialPointX = { x: number };
type Point = PartialPointX & { y: number };
let point: Point = {
x: 1,
y: 1,
};
上面的例子中,我们先定义了PartialPointX
类型,它有一个x
属性。然后使用&
运算符创建一个新的Point
类型,它包含了PartialPointX
和{ y: number }
的所有属性。这样point
对象就同时拥有了x
和y
属性。
交叉类型常用于混入(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
不是string
和number
的共有属性。要解决这个问题,需要使用类型断言:
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)。除了typeof
和instanceof
运算符外,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
可能是Bird
或Fish
类型。我们可以使用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;
}
}