mypy --strict 必须进 CI,因为它能在运行前发现87%的参数类型错误,通过全项目AST扫描在调用处标红问题(如str传给期望dict的函数),而非等到深层报AttributeError;Literal和TypedDict提供字段存在性与值域校验,配合pydantic或cast才能闭环;IDE推断不可靠且不一致,而mypy结合stub包实现92%+覆盖,CI失败即阻断合并,保障接口契约不被破坏。

mypy 能在代码运行前就发现 87% 的参数类型错误,不是“应该加”,而是“不加就会在 CI 或上线后爆 AttributeError 或 KeyError”。
为什么 mypy --strict 必须进 CI?
大型项目里,函数调用链常跨模块、跨团队。一个 State 对象被传进五个函数后,如果第四个函数误把 str 当 dict 解构,报错堆栈会指向最深层的 state["user_id"],但问题根源其实在上游——而 mypy 在第一行调用时就标红了。
-
mypy不依赖运行时数据,扫描 AST 即可发现def process(data: dict) -> list却被传入str -
--strict模式强制检查未注解参数、隐式Any、缺失返回值等易漏点 - CI 中失败即阻断合并,避免“我本地跑通就行”的侥幸——尤其对
LangChain和FastAPI这类靠类型驱动路由/序列化的框架,漏检等于接口契约失效
Literal 和 TypedDict 为什么不是炫技?
AI 项目中 LLM 返回 JSON 字段名不稳定,比如 "user_role" 今天叫 "role",明天变 "userType"。用 Literal["admin", "guest"] 注解参数,mypy 就能拦截非法字符串;用 TypedDict 定义响应结构,pydantic 解析时自动补缺字段,而不是等到业务逻辑里 if data.get("role") == "admin" 突然 None。
-
Literal不是限制运行时输入,而是告诉类型检查器:“这个变量只可能取这几个值”,否则if role == "admin"会被判为死分支 -
TypedDict比Dict[str, Any]多一层字段存在性校验,mypy会报TypedDict key "role" not present,而不是运行时报KeyError - 二者都需配合
cast()或运行时验证(如pydantic.parse_obj_as())才能闭环,光写注解没用
为什么不能只靠 IDE 提示?
IDE 的类型推断基于当前文件上下文和缓存,对跨文件继承、动态构造对象(如 getattr(module, name)())、或 __getattr__ 魔术方法基本失效。而 mypy 是全项目符号表分析,且支持 types-requests、pandas-stubs 等第三方 stub 包,覆盖率达 92%+。
- VS Code + Pylance 可能提示
response.json()返回dict,但真实返回可能是list或None——mypy结合pydantic类型定义才能真正约束 - 团队里有人关掉 IDE 类型检查,或用 Vim/Neovim,单靠 IDE 就失去一致性保障
-
mypy的错误信息比 IDE 更精确,例如明确指出Argument 1 to "handle" has incompatible type "str"; expected "ChatMessage"
mypy 报错也不等于代码正确。它只保证“契约被声明且被遵守”,但契约本身是否合理(比如把 LLM 输出直接注解为 Literal),还得靠运行时验证兜底。


















