TypeScript中的类型谓词可以帮助你根据条件来缩小你的类型。它们类似于类型守卫,但在函数上工作。它们的工作方式是,如果一个函数返回真,就把参数的类型改成更有用的东西。
让我们从一个基本的例子开始。假设你有一个检查某个值是否为字符串类型的函数。
function isString(s) {
return typeof s === 'string';
}
在另一个函数中使用isString 函数。
function toUpperCase(x: unknown) {
if(isString(x)) {
x.toUpperCase(); // ⚡️ x is still of type unknown
}
}
TypeScript会抛出一个错误。我们可以确定x ,在这一点上是字符串类型。但由于验证被包裹在一个函数中,x 的类型不会改变(与类型守护相反)。进入类型谓词。
让我们明确地告诉TypeScript,如果isString 评估为真,参数的类型就是字符串。
function isString(s): s is string {
return typeof s === 'string';
}
TypeScript现在知道我们在我们的toUpperCase 函数中处理的是字符串。
function toUpperCase(x: unknown) {
if(isString(x)) {
x.toUpperCase(); // ✅ all good, x is string
}
}
缩小集合的范围#
这不仅可以帮助你处理未知的类型,或多个类型,还可以缩小一个类型中的集合。让我们有一个程序,你扔一个骰子。每当你扔出一个六,你就赢了。
function pipsAreValid(pips: number) {
// we check for every discrete value, as number can
// be something between 1 and 2 as well.
return pips === 1 || pips === 2 || pips === 3 ||
pips === 4 || pips === 5 || pips === 6;
}
function evalThrow(count: number) {
if (pipsAreValid(count)) {
// my types are lying 😢
switch (count) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log('Not today');
break;
case 6:
console.log('Won!');
break;
case 7:
// TypeScript does not complain here, even though
// it's impossible for count to be 7
console.log('This does not work!');
break;
}
}
}
这个程序起初看起来不错,但从类型的角度看有一些问题:count 是数字类型。这作为一个输入参数是可以的。我们马上验证count 是一个1到6之间的数字。一旦我们验证了这一点,count 就不再是任何数字了。它被缩小到一个离散的六个值的集合。
因此,从switch语句开始,我的类型就在说谎了为了防止进一步的复杂化,让我们使用联合类型将数字集缩小到这六个离散值。
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
function pipsAreValid(pips: number): pips is Dice {
return pips === 1 || pips === 2 || pips === 3 ||
pips === 4 || pips === 5 || pips === 6;
}
function evalThrow(count: number) {
if (pipsAreValid(count)) {
// count is now of type Dice 😎
switch (count) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log('Not today');
break;
case 6:
console.log('Won!');
break;
case 7:
// TypeScript errors here. 7 is not in the union type of
// Dice
console.log('This does not work!');
break;
}
}
}
对我们和我们的同事来说,类型安全了很多。当然,这种 "类型转换 "可以是任何有意义的东西,以加强你的应用程序。即使你验证了复杂的对象,你也可以将你的参数缩小到一个特定的类型,并确保它们与你代码的其他部分相处。很有用,特别是当你依赖大量的函数时。