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 });
}
我们提取了一个isAuthenticated
帮助程序,它可以告诉我们用户是否已登录。如果没有登录,我们会确保抛出错误。
如果使用纯 JavaScript,我们现在应该已经完成了。如果user
是,则会抛出一个错误null
,所以当我们到达数据库语句时,我们确信该user
对象已经定义。
另一方面,TypeScript 仍然会将user
as视为类型User | null
。为了解决这个问题,我们可以引入类型保护。通过添加类型谓词来更新辅助函数user is User
,它会理解 user 位于语句null
的范围内if
,因此必须在User
其之后。
function isAuthenticated(user: User | null): user is User {
return typeof user !== null;
}
如果您不熟悉类型保护和类型谓词,我建议您阅读我之前关于类型保护和类型谓词的文章。
通过添加类型谓词,我们修复了数据库语句中的问题。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 });
}
在纯 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');
}
}
现在,当您调用此函数时,TypeScript 知道的值user
永远不能null
在该行之后。
function addComment(user: User | null, comment: string) {
assertAuthenticated(user);
db.comments.insert({ author: user._id, comment });
}
断言通用
现在我们知道了asserts
,我们还可以很容易地引入一个可重用的辅助函数:
function assert<T>(
condition: T,
message,
): asserts condition is Exclude<T, null | undefined> {
if (condition === null || condition === undefined) {
throw new Error(message);
}
}
然后,每当您有一个接受可选值或部分值的函数时,您都可以使用 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`
}
当blog
在数据库中找不到时,该assert
语句将引发链中的错误,如果确实找到了某些内容,我们可以在 assert 调用后安全地使用该对象。
下次考虑使用 类型转换属性时as MyType
,不妨考虑编写类型断言。这样,您无需简单地屏蔽 TypeScript,只需一行代码即可获得运行时验证。
👋 我是 Stephan,我正在创建metricmouse.com。如果您想阅读更多我的资讯,请在Twitter上关注我。
文章来源:https://dev.to/smeijer/typescript-type-assertions-4klf