TypeScript 中的 Unknown 类型为何有用

原文信息: 查看原文查看原文

Why Unknown Types Are Useful

- Michael Uloth

当外部数据进入您的程序时,除非您验证了它,否则您无法真正确定它的类型。那个库的输出,那个API响应,尤其是那个用户输入……您确定它就是您认为的那样吗?

在您检查之前,给数据分配的最准确的类型应该是意味着“我实际上不知道”。1

做出假设

假设您正在从用户那里获取输入,并期望它是一个字符串。一个天真的方法是假设数据总是您期望的那样:

const getUserInput = (): string => {/*...*/}

const unsafe = () => {
  const data = getUserInput()
  data.toUpperCase()
}

没有警告,没有问题,对吧?并不完全……因为我们告诉类型检查器用户输入总是一个string。这就是为什么它愿意让我们在data上调用字符串方法。

但是,如果data有时是undefined(或任何不是string的东西)怎么办?在这种情况下,这段代码在运行时将遇到一个未捕获的TypeError,说Cannot read properties of undefined (reading 'toUpperCase'),这可能会使您的程序处于破损状态。

这就是“Unknown”可以帮忙的地方。2

Unknown 来救援

一些语言明确包含了一个叫做“Unknown”的类型(例如TypeScript就有一个),而其他语言则有一个您可以类似对待的类型(例如Python的object类型实际上意味着“Unknown”)。

无论您的语言给您什么,一般的方法都是相同的:

  1. 将“Unknown”类型分配给未经验证的数据
  2. 在使用它们之前明确验证数据的相关特征
  3. 当您的工具警告您正在做出不安全的假设时感到高兴

不做假设

让我们要求类型检查器帮助我们更加小心:

// 🤞 应该是一个字符串,但谁知道呢...
const getUserInput = (): unknown => {/*...*/}

const unsafe = () => {
  const data = getUserInput()
  data.toUpperCase()
  // 🚨 'data' 是 'unknown' 类型
}

完美!我们想要那些类型警告。我们在我们尚未确认是string的值上调用了一个string方法(toUpperCase)。这是有风险的。

为了解决警告,我们需要验证我们对data类型的假设:

const getUserInput = (): unknown => {/*...*/}

const safe = () => {
  const data = getUserInput()

  if (typeof data === 'string') {
    data.toUpperCase() // 确认安全
  } else {
    // 处理无效输入
  }
}

随着您验证的每一个假设,类型检查器从其狭窄的起点(unknown)“扩展”了对您的数据的理解,到具有更多特征的类型(例如string)。

现在您可以确定您拥有的数据类型了。

脚注

  1. 这篇文章是对我在Reddit上收到的问题的回应,这些问题建议我们总是知道给定值的数据类型。对于任何来自程序外部的数据,我认为这种假设是有风险的!

  2. 这是很多人会使用“any”类型的地方,但要小心!“any”是“unknown”的反面,通过告诉类型检查器关于一个值的所有假设都是安全的,有效地禁用了类型检查。您可能不想要那个。

分享于 2024-08-12

访问量 20

预览图片