Typescript 类型断言

2025-06-07

Typescript 类型断言

类型断言看起来很像类型保护,不同之处在于它们不需要嵌入在if语句中。

假设我们有一个博客,允许经过身份验证的用户发表评论。我们想出了一个这样的函数:

function isAuthenticated(user: User | null) {
  return typeof user !== null;
}

function addComment(user: User | null, comment: string) {
  if (!isAuthenticated(user)) {
    throw new Error('unauthenticated')
  }

  db.comments.insert({ author: user._id, comment });
}
Enter fullscreen mode Exit fullscreen mode

我们提取了一个isAuthenticated帮助程序,它可以告诉我们用户是否已登录。如果没有登录,我们会确保抛出错误。

如果使用纯 JavaScript,我们现在应该已经完成​​了。如果user是,则会抛出一个错误null,所以当我们到达数据库语句时,我们确信该user对象已经定义。

另一方面,TypeScript 仍然会将useras视为类型User | null。为了解决这个问题,我们可以引入类型保护。通过添加类型谓词来更新辅助函数user is User,它会理解 user 位于语句null的范围内if,因此必须在User其之后。

function isAuthenticated(user: User | null): user is User {
  return typeof user !== null;
}
Enter fullscreen mode Exit fullscreen mode

如果您不熟悉类型保护和类型谓词,我建议您阅读我之前关于类型保护和类型谓词的文章。

通过添加类型谓词,我们修复了数据库语句中的问题。TypeScript 知道user永远不会到达null那个阶段。

断言函数

问题在于重复。这三行代码遍布整个项目,会造成干扰。此外,这不太可能是你定义的唯一检查。

我们可以把这张支票变成这样:

function assertAuthenticated(user: User | null): user is User {
  if (user === null || user === undefined) {
    throw new Error('unauthenticated');
  }

  return true;
}

function addComment(user: User | null, comment: string) {
  assertAuthenticated(user);

  db.comments.insert({ author: user._id, comment });
}
Enter fullscreen mode Exit fullscreen mode

在纯 JavaScript 中,这仍然可以工作。assertAuthenticated如果user对象未定义,函数将抛出异常,并且由于错误会传播,我们永远无法到达数据库语句。

然而,由于我们删除了包装if语句,TypeScript 再次出现问题。user._id数据库语句中的 会抛出TS2531: Object is possibly 'null'。为了解决这个问题,我们将插入Type assertion

其实很简单。只需asserts在类型谓词前面添加 ,并从断言函数中删除 return 语句即可。类型保护必须返回boolean,而断言函数必须返回void

function assertAuthenticated(user: User | null): asserts user is User {
  if (user === null || user === undefined) {
    throw new Error('unauthenticated');
  }
}
Enter fullscreen mode Exit fullscreen mode

现在,当您调用此函数时,TypeScript 知道的值user永远不能null在该行之后。

function addComment(user: User | null, comment: string) {
  assertAuthenticated(user);
  db.comments.insert({ author: user._id, comment });
}
Enter fullscreen mode Exit fullscreen mode

断言通用

现在我们知道了asserts,我们还可以很容易地引入一个可重用的辅助函数:

function assert<T>(
  condition: T,
  message,
): asserts condition is Exclude<T, null | undefined> {
  if (condition === null || condition === undefined) {
    throw new Error(message);
  }
}
Enter fullscreen mode Exit fullscreen mode

然后,每当您有一个接受可选值或部分值的函数时,您都可以使用 thisassert帮助程序简单地保护它们。

async function latestBlog() {
  const blog = await db.blogs.findOne({ author: 'smeijer' }); // Blog | null
  assert(blog, 'author does  not have any blogs');

  // and here we know `blog: Blog`
}
Enter fullscreen mode Exit fullscreen mode

blog在数据库中找不到时,该assert语句将引发链中的错误,如果确实找到了某些内容,我们可以在 assert 调用后安全地使用该对象。

下次考虑使用 类型转换属性时as MyType,不妨考虑编写类型断言。这样,您无需简单地屏蔽 TypeScript,只需一行代码即可获得运行时验证。


👋 我是 Stephan,我正在创建metricmouse.com。如果您想阅读更多我的资讯,请在Twitter上关注我。

文章来源:https://dev.to/smeijer/typescript-type-assertions-4klf
PREV
TypeScript 类型保护和类型谓词
NEXT
停止在 map、reduce 和 forEach Map forEach Reduce 中进行变异