Skip to content

第 32 条:避免在类型别名中包含 nullundefined

要点

  • 避免定义包含 nullundefined 的类型别名。

正文

在这段代码中,问号链(?.)是必须的吗?user 有可能是 null 吗?

ts
function getCommentsForUser(comments: readonly Comment[], user: User) {
  return comments.filter((comment) => comment.userId === user?.id)
}

💻 playground

即使开启了 strictNullChecks,没有看到 User 的定义也无法判断。如果 User 是允许 nullundefined 的类型别名,那么可选链(?.)是需要的:

ts
type User = { id: string; name: string } | null

💻 playground

但如果是普通的对象类型,则不需要:

ts
interface User {
  id: string
  name: string
}

💻 playground

一般来说,最好避免定义允许 nullundefined 的类型别名。虽然类型检查器不会因为你违反这个规则而报错,但阅读你代码的人会感到困惑。看到 User 这个类型名时,我们默认它代表一个用户,而不是“可能是用户也可能不是”。

如果因为某些原因必须包含 null,最好给类型起一个明确的名字,方便阅读:

ts
type NullableUser = { id: string; name: string } | null

💻 playground

但为什么不直接用更简洁且通用的写法 User | null 呢?

ts
function getCommentsForUser(comments: readonly Comment[], user: User | null) {
  return comments.filter((comment) => comment.userId === user?.id)
}

💻 playground

这个规则针对的是类型别名的顶层结构,不针对对象中的属性是 nullundefined 或可选的情况,比如:

ts
type BirthdayMap = {
  [name: string]: Date | undefined
}

💻 playground

但不要这样写:

ts
type BirthdayMap = {
  [name: string]: Date | undefined
} | null

💻 playground

避免对象类型中出现 null 和可选字段也有理由,不过那是后面章节(第 33 和第 37 条)才讲的内容。现在的建议是避免让你的类型别名让代码阅读者困惑,尽量用表示具体东西的类型别名,而不是表示“somethingnullundefined”的类型别名。

关键点总结

  • 尽量避免定义包含 nullundefined 的类型别名。

Released under the MIT License.