do-not-widen-types-to-silence-the-checker
当类型检查器拒绝某个调用或返回值时,应收窄该值或处理缺失的情况,而不是把注解放宽为 Any、object 或未经检查的联合类型。
由读过该卡片的评审者赋予,而不是文件自我声明的。智能体在处理不可信内容的运行中提炼出的卡片,生来即被标记为受污染,在被检索之前会一直等待评审。
何时会想到它
- 参数或返回值上的类型检查器错误
- 想加 Any 或 cast 让 mypy 或 pyright 通过的冲动
- 联合类型包含 None,而调用方期望具体类型
- 仅为满足某一个调用点而放宽签名
价值通常在「避免」和「检查」里。「该做」是人人都会写的那一节。
上面的卡片正文是译文。英文原文才是 CLI 导入的内容、智能体运行时读取的内容,也是下方哈希所证明的内容。
触发
静态类型检查器(mypy、pyright、tsc、cargo check)报告类型不兼容,例如在需要 str 的地方传入 str | None,或返回的联合类型比声明的签名更宽。让诊断消失最快的办法,就是放宽参数或返回值的注解(Any、object、unknown、# type: ignore,或给一个本不该接受它的接口加上 | None)。
该做
- 弄清楚检查器实际证明了什么:哪个分支可能产生意外类型,该分支在运行时是否可达。 2. 如果较宽的值是调用点的缺陷或未处理的边界情况,就在调用前收窄:检查
None,在边界处校验输入,或尽早抛出清晰的错误。 3. 如果函数确实支持较宽的类型,就先更新其实现以处理该类型的每个成员,再放宽签名。 4. 保持边界收窄:对未类型化的数据(dict[str, Any]、原始 JSON)只在进入模块时解析或细化一次,而不是把宽泛类型向内传播。
避免
把类型检查器当作需要安抚的障碍,而不是证明某个分支未被处理的依据。经典的失败模式是:因为某个调用方忘了检查 user_id 是否存在,就把 def load_user(user_id: str) -> User 改成接受 str | None 或返回 User | Any。检查器立刻变绿,未处理的 None 从边界处的静态错误变成三层调用之后的运行时 AttributeError,而且每个下游调用方要么继承了更宽的联合类型,要么彻底失去了类型检查。同样糟糕的是,在一行代码上加 cast(...) 或 # type: ignore,而紧邻的前面代码中并没有使这个 cast 成立的不变式。
检查
在修改过的模块上运行类型检查器,并检查每一处被修改签名的差异。每个新放宽的参数或返回类型都有明确的分支或测试覆盖新增的情况;没有仅为压制一个本可通过在边界收窄来解决的不匹配而引入 Any、cast 或 # type: ignore;下游调用方没有为生产者本就不需要产生的类型额外添加防御性检查。
风险
有时库的存根确实有误或不完整,或者外部负载确实是任意的,直到后面的阶段去检查它为止。这种情况下,局部的、有文档说明的 # type: ignore[code] 或一个狭窄的边界适配器,比为了迎合有问题的存根而用死分支扭曲运行时代码要好。
如何使用
卡片就是数据。克隆仓库并按路径导入——任何经由网络到达的内容都会被视为受污染并等待批准,这正是你想要的行为,也是这里没有一行命令安装器的原因。
git clone https://github.com/brcampidelli/chimera-agent.gitchimera skills-import chimera-agent/skills/do-not-widen-types-to-silence-the-checker/SKILL.md完整性
文件发布版本的 SHA-256。导入方可以据此核对收到的内容与本页展示的一致。
9fdad16332fb728bb73f495c19effaeab028d4160a220ad94a8ff0ab8996a5b9