引言:为什么“能编译通过”是一个糟糕的标准
想象一下这个场景:你在 GitHub 上打开一个 Pull Request,看到 CI 的绿色勾号,内心有个声音说:“类型检查通过了,说明代码是正确的。”熟悉吗?如果是,那你就掉进了我们称之为 “TypeScript 的虚假安全感” 的陷阱。
TypeScript 不是万能药。它无法捕获逻辑错误,不检查业务需求,更无法保证你的代码在运行时不会崩溃。正因如此,TypeScript 代码审查 不是“扫一眼”就完事,而需要一套系统的方法。在这篇文章中,我将告诉你,在审查 TypeScript 代码时真正应该关注什么。我在 PythonLib 上写博客,我们习惯深入挖掘——请做好准备。
1. any 类型:危险的信号
最明显但也是最常见的问题。any 是 TypeScript 的开关。如果你在代码中看到 any,你的任务是弄清楚:这是作者偷懒,还是确实有正当理由?
糟糕的例子:
function processData(data: any) { return data.name.toLowerCase();}这里我们丢失了所有类型信息。如果 data 传入的是 null 或 undefined——运行时就会报错。
好的例子:
interface UserData { name: string; age?: number;}
function processData(data: UserData) { return data.name.toLowerCase();}代码审查时检查什么: 如果仍然使用了 any(例如,处理结构未知的 JSON),作者是否要求 在输入时进行验证?通常,any 掩盖了外部数据缺乏正常类型化的问题。
2. 严格模式:不要禁用它
我在 tsconfig.json 中首先检查的是 strict: true 标志。如果没有它,你就失去了大量检查:strictNullChecks、noImplicitAny、strictFunctionTypes 等。
没有 strictNullChecks,你可能会写出:
const user = getUser(); // 可能返回 nullconst name = user.name; // TS 不会报错,但运行时就会崩溃在代码审查时: 如果你发现项目没有使用严格模式——请发出警报。这是技术债务,会呈指数级增长。最好花一天时间启用严格模式并修复错误,而不是花一个月时间在生产环境中排查 bug。
3. 泛型:力量与责任
泛型是 TypeScript 的超能力。但能力越大,责任越大。在代码审查时,我总会看:作者是否过度复杂化了类型?
过度复杂的例子:
function identity<T extends Record<string, unknown>>(arg: T): T { return arg;}如果我们只是返回相同的内容,为什么要把 T 限制为 Record<string, unknown>?这样就可以了:
function identity<T>(arg: T): T { return arg;}检查什么:
- 泛型是否用在需要灵活性的地方,还是可以用具体类型代替?
- 是否存在阻碍重用的冗余约束(extends)?
- 类型推断是否正确——用户是否必须手动指定
<SomeType>,而 TypeScript 本可以自行推断?
4. unknown vs any:选择正确的工具
当你处理类型未知的数据时(API、localStorage、用户输入),请使用 unknown,而不是 any。unknown 强制你在使用前显式检查类型。
不好的做法:
<