Skip to content

真值缩小

真值检查是我们在 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); 
  } 
}