Appearance
真值缩小
真值检查是我们在 JavaScript 中经常做的一件事。在 JavaScript 中,我们可以在条件、 && 、 || 、 if 语句、布尔否定 ( ! ) 等中使用任何表达式。例如, if 语句不希望它们的条件总是具有类型 boolean 。
typescript
function getUsersOnlineMessage(numUsersOnline: number) {
if (numUsersOnline) {
return `现在共有 ${numUsersOnline} 人在线!`;
}
return "现在没有人在线. :(";
}
在 JavaScript 中,像这样的 if 条件语句,首先将它们的条件“强制”转化为 boolean 以使其有意义,然后根据结果是 true 还是 false 来选择它们的分支。像下面这些值:
- 0
- NaN
- "" (空字符串)
- 0n ( bigint 零的版本)
- null
- undefined
以上所有值强制都转换为 false ,其他值被强制转化为 true 。你始终可以在 Boolean 函数中运行值获得 boolean ,或使用较短的双布尔否定将值强制转换为 boolean 。(后者的优点是 TypeScript 推断出一个狭窄的文字布尔类型 true ,而将第一个推断为 boolean 类型。)
typescript
// 这两个结果都返回 true
Boolean("hello"); // type: boolean, value: true
!!"world"; // type: true, value: true
利用这种行为是相当流行的,尤其是在防范诸如 null 或 undefined 之类的值时。例如,让我们尝试将它用于我们的printAll 函数。
typescript
function printAll(strs: string | string[] | null) {
if (strs && typeof strs === "object") {
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
}
}
你会注意到我们已经通过检查 strs 是否为真,消除了上述错误。这至少可以防止我们在运行代码时出现可怕的错误,例如:
typescript
TypeError: null is not iterable
但请记住,对原语的真值检查通常容易出错。例如,考虑改写 printAll :
typescript
function printAll(strs: string | string[] | null) {
// !!!!!!!!!!!!!!!!
// 别这样!
// 原因在下边
// !!!!!!!!!!!!!!!!
if (strs) {
if (typeof strs === "object") {
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
}
}
}
我们将整个函数体包装在一个真实的检查中,但这有一个小的缺点:我们可能不再正确处理空字符串的情况。
TypeScript 在这里根本不会报错,但是如果你不太熟悉 JavaScript,这是值得注意的行为。TypeScript 通常可以帮助你及早发现错误,但是如果你选择对某个值不做任何处理,那么它可以做的就只有这么多,而不会考虑过多的逻辑方面的问题。如果需要,你可以确保使用 linter(程序规范性) 处理此类情况。
关于通过真实性缩小范围的最后一点,是通过布尔否定 ! 把逻辑从否定分支中过滤掉。
typescript
function multiplyAll(
values: number[] | undefined,
factor: number
): number[] | undefined {
if (!values) {
return values;
} else {
return values.map((x) => x * factor);
}
}