它比单元测试早一步、比人看便宜两个数量级。代价是它的判断到类型为止,再往下(这个值合不合法)一概不管。
把新代码所在目录的最严档打开,报出来的错误能不能一次降到 0,而且不是靠加忽略?
靠 # type: ignore 把数压到 0 不算——那一处的检查被永久关掉,而且没有记录。降不到 0 说明这次改动要连带改的接口比预计的多,先只对新写的文件开,老目录挂在低档。
它管的是「形状对不对」,不管「值对不对」。两类错误的分界很干脆,按改动长什么样选谁去挡。
| 改动长什么样 | 类型检查 | 单元测试 |
|---|---|---|
| 调用时少传一个参数 | 报参数个数不符 | 跑到那一行才报 |
| 返回结构换了字段名 | 报结构里没有这个键 | 断言里没写这个字段就测不出 |
| 可能是空的值直接取属性 | 报空值联合类型没有该属性 | 只有构造出空值的那条用例能发现 |
| 传进来的值来自解析外部输入 | 不报(它是任意类型) | 取决于用例写没写 |
| 金额不能为负 | 不报 | 报 |
| 越权读别人的记录 | 不报 | 报(用例里写了才行) |
第四行是这一项的边界:解析外部输入得到的任意类型,标注成它就是在这一处把检查关掉,而且关得没有记录。所以解析那一层必须立刻收窄成具体类型,里面才全是硬的类型。
类型检查给的是「哪两端对不上」,不是「值对不对」。一条能用的报错要能直接指到改哪一行。
backend/app/handler/list.go:31:18 返回 *Item,接口要求 *ItemDTO 规则 no-any-return / missing-return-type
要认的是这个形状:文件、行、哪两端对不上三样齐全。只报「有类型错误」而指不到具体位置,等于把几百条一次性丢给人。
- return item + return item as any 或 # type: ignore(不带规则号)
认它的办法:搜 any 与不带规则号的 ignore。每一处都等于在这一行把检查关掉,而且关得没有记录——报错消失了,两端的形状还是对不上。按不许改判据退回。
场景:助手把接口返回结构里的字段名改了,调用方还在用旧名字,测试只覆盖了主流程那一条。
老仓库一次打开最严档会报几百条,没人会一次改完;实际结果是有人把配置关掉,或者批量加忽略,两种都比渐进更差。做法是按目录分档。
| 档位 | 打开后新报什么 | 用在哪些目录 |
|---|---|---|
| 第一档:默认 | 明显的类型冲突 | 老目录 |
| 第二档:要求标注 | 没写参数与返回类型的函数 | 正在整理中的目录 |
| 第三档:最严 | 返回值是任意类型、隐式可空、变量重复定义 | 新代码所在的目录 |
落法:新代码按第三档,老目录挂在第一档,每个季度把一个目录升上来。升级本身是一次单独的合并请求,不夹带功能改动——否则报出来的几百条会混在这次的功能差异里,评审无法判断哪条是自己引入的。
两个静默失效点要记住:分档是按模块名匹配的,模块改名会让那条配置失效却不报错;把「忽略缺失的导入」写在全局,等于全仓库退回第一档,同样一条错都不会报。放开范围必须写在具体模块上,不要写在全局配置里。