联合类型适合于那些值可以为不同类型的情况, 但当我们想确切地了解是否为 某个类型时怎么办? JavaScript里常用来区分2个可能值的方法是检查成员是否存在。
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
return
}
let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim(); // errors
// 每一个成员访问都会报错
if (pet.swim) {
pet.swim();
}
else if (pet.fly) {
pet.fly();
}
为了让这段代码工作,我们要使用类型断言:
let pet = getSmallPet();
if ((<Fish>pet).swim) {
(<Fish>pet).swim();
}
else {
(<Bird>pet).fly();
}
假若我们一旦检查过类型,就能在之后的每个分支里清楚地知道 pet的类型的话就好了。
TypeScript里的 类型保护机制让它成为了现实。 类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 要定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个 类型谓词:
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
在这个例子里, pet is Fish就是类型谓词。 谓词为 parameterName is Type这种形式, parameterName必须是来自于当前函数签名里的一个参数名。
每当使用一些变量调用 isFish时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。
// 'swim' 和 'fly' 调用都没有问题了
let pets: Bird = {
fly: function() {
console.log('fly')
},
layEggs: function() {
}
};
if (isFish(pets)) {
pets.swim();
}
else {
pets.fly();
}
注意TypeScript不仅知道在 if分支里 pet是 Fish类型; 它还清楚在 else分支里,一定 不是 Fish类型,一定是 Bird类型。
完整代码如下:
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
return {
fly: function() {
console.log('fly')
},
layEggs: function() {
}
}
}
let pet = getSmallPet();
pet.layEggs(); // okay
//pet.swim(); // errors
if ((<Fish>pet).swim) {
(<Fish>pet).swim()
}
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined
}
if (isFish(pet)) {
pet.swim()
} else {
pet.fly()
}
有个奇怪的地方,如果我重新定义一个Bird变量,代码没有问题
let pet1: Bird = {
fly: function() {
console.log('fly')
},
layEggs: function() {
}
};
if (isFish(pet1)) {
pet1.swim()
} else {
pet1.fly() //ok
}
但是如果定义一个Fish的变量,则会报错
let pet2: Fish = {
swim: function() {
console.log('swim')
},
layEggs: function() {
}
};
if (isFish(pet2)) {
pet2.swim()
} else {
pet2.fly() //error
}
但是通过函数的方式,两个定义都可以正常显示
function getSmallPet(): Fish | Bird {
// ...
return {
fly: function() {
console.log('fly')
},
layEggs: function() {
}
}
}
let pet = getSmallPet();
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined
}
if (isFish(pet)) {
pet.swim()
} else {
pet.fly() //ok
}
function getSmallPet(): Fish | Bird {
// ...
return {
swim: function() {
console.log('swim')
},
layEggs: function() {
}
}
}
let pet = getSmallPet();
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined
}
if (isFish(pet)) {
pet.swim()
} else {
pet.fly() //ok
}