给一个 API 响应加上类型断言,并不会改变它在运行时的形状。网络数据、用户输入和本地配置,都可能与我们的预期不同。

类型系统最有价值的用法之一,是提醒我们在哪里还没有建立信任。

让未知保持未知

外部数据进入程序时,使用 unknown 能让不确定性保持可见。只有经过校验,才能进入可信的内部逻辑。

lib/parse-author.ts
type Author = { name: string; bio: string }
 
function parseAuthor(value: unknown): Author {
  if (
    typeof value !== 'object' ||
    value === null ||
    !('name' in value) ||
    !('bio' in value) ||
    typeof value.name !== 'string' ||
    typeof value.bio !== 'string'
  ) {
    throw new Error('Invalid author data')
  }
 
  return { name: value.name, bio: value.bio }
}

这个函数做了两件事:验证输入,并创建一个符合内部约定的值。后面的代码不需要重复同一组防御性判断。

让错误靠近来源

如果一篇文章缺少标题,最好在读取文章时就指出文件名和缺失字段,而不是等到页面渲染出一个空白区域。

错误越靠近来源,定位的成本越低。相比笼统的「加载失败」,明确的错误信息也是维护者体验的一部分。

不要把所有可能性带进内部

在边界完成转换之后,内部模型可以更严格。比如把日期统一成 ISO 字符串,把可选标签统一成空数组。

这样,列表组件只需要负责展示。它不必知道数据来自文件、数据库还是 API,也不必在每一行渲染时重新处理缺失值。

从一个边界开始

不需要一次重构整个项目。选择一个经常出错的数据入口,补上校验和清晰的错误信息,再观察下游代码是否因此变简单。

好的类型设计,并不是消灭现实中的不确定性,而是给它一个明确的位置。

感谢你读到这里。

思考没有终点,下一篇见。