TypeScript 类型体操的边界
写过一段时间 TypeScript 的人都会经历一个阶段:沉迷条件类型、模板字面量类型和递归类型,试图让类型系统「证明一切」。这个阶段很有趣,但它和生产代码的健康关系不大。我给自己划了三条边界。
边界一:类型复杂度必须换来调用方的错误减少
写一个五层的条件类型之前,先问:这个类型挡住的是真实发生过的 bug,还是想象中的 bug?如果一个联合类型只有两个成员,手写两个重载比写一套泛型体操更便宜、更可读。
类型体操的收益在边界处最大——库的 API 表面、跨团队共享的模型、数据解析层。业务页面内部的数据流,通常不值得。
边界二:错误信息是类型的一部分
一个复杂类型如果报错时让人读不懂,它就没有完成本职工作。好的类型出错时指向具体位置、给出可读的冲突说明;坏的体操会报一屏展开十几层的结构。
写完复杂类型后,故意传一个错的值看看报错长什么样。如果连你自己都要读三遍,调用方会直接 as any 绕过去——类型体操在那一刻就白写了。
边界三:性能与维护者都是成本
递归条件类型在大型代码库里的编译成本是真实的。一个让编辑器卡顿的类型,每天都在向团队所有成员收税。
维护成本同样如此:类型体操的可读性通常只有作者在写完后的两周内持有。三个月后回来看 T extends infer U ? U extends infer V ? ...,没有人愿意碰它。
我的结论
类型体操是语言特性,不是工程美德。我的默认顺序是:先想运行时校验能不能解决问题(zod 之类的运行时 schema 比纯类型更适合信任边界),再考虑简单的联合与泛型,最后才是体操。多数项目停在第二层就够了。