Cursor / Claude Code / Copilot 写完一段 TypeScript 直接 commit,你跑 tsc --noEmit 看到一堆 TS2322: Type 'string' is not assignable to type 'number' 或 TS2339: Property 'foo' does not exist on type 'unknown'。AI 模型没有运行时类型反射能力,只能凭训练数据猜接口形状,遇到 generics、union types、third-party .d.ts 时尤其容易出错。这篇拆 5 个高频根因,给出让 AI 自动在循环里修类型错的工作流。
常见原因
按命中率从高到低排序。
1. AI 猜了它没读过的类型签名
最常见:调用第三方库时,AI 凭记忆里旧版本的签名写代码,但你装的是新版本,API 已经改了。
// AI 写的(旧 stripe SDK)
const charge = await stripe.charges.create({ amount: 1000, currency: "usd" });
// 新 SDK(PaymentIntents 时代)
// → TS2554: Expected 0 arguments, but got 1
如何判断:错误码是 TS2554(参数数量错) / TS2345(参数类型错) / TS2339(属性不存在),且发生在第三方库调用点。
2. 强转 any / as unknown as T 掩盖真错误
AI 跑不通就 as any 或 as unknown as Foo 一刀切,编译过了但运行时直接 crash。
const data = (response as any).user.email; // response 其实是 { error: string }
// 编译通过,运行时 TypeError: Cannot read property 'email' of undefined
如何判断:grep -n "as any\|as unknown as" src/ 数一下出现次数;AI 写的代码段里 > 2 处基本都是回避问题。
3. 泛型参数漏写或写错
Array.prototype.reduce、useState、useRef、Map / Set、自定义 generic function——AI 经常默认 TypeScript 能推出来,但实际推成 never[] 或 unknown。
// AI 写的
const items = [].reduce((acc, x) => {
acc.push(x.name); // TS2339: Property 'push' does not exist on type 'never'
return acc;
}, []);
// 正确
const items = [] as string[];
// 或
const items = [].reduce<string[]>((acc, x) => { acc.push(x.name); return acc; }, []);
如何判断:错误信息里出现 never / unknown / Argument of type 'X' is not assignable to parameter of type 'never'。
4. strict 模式下没处理 null / undefined
AI 训练里大量旧代码没开 strictNullChecks,结果生成的代码假设字段一定存在。开了 strict 之后 TS2532: Object is possibly 'undefined' 满天飞。
function getEmail(user: User | null) {
return user.email; // TS18047: 'user' is possibly 'null'
}
如何判断:错误码 TS2531 / TS2532 / TS18047 / TS18048,且 tsconfig.json 里 "strict": true。
5. import 类型与值混用 / 缺 import type
verbatimModuleSyntax 或 isolatedModules 开启后,type-only import 必须用 import type,AI 经常忘。
import { User } from "./types"; // 如果 User 只是 type,TS1484 / runtime "undefined"
// 正确
import type { User } from "./types";
如何判断:错误码 TS1484: 'User' is a type and must be imported using a type-only import 或运行时 User is undefined。
最短修复路径
按收益排序。Step 1+2+3 是 AI 类型错误的标准修复闭环。
Step 1:跑 tsc --noEmit 把所有错一次性暴露
不要看到第一个错就让 AI 改。一次性输出全部,让 AI 看见全貌:
npx tsc --noEmit --pretty false > ts-errors.txt
wc -l ts-errors.txt
--pretty false 关闭彩色,方便贴给 AI;--incremental false 可以保证每次重新算(如果 .tsbuildinfo 让你怀疑结果)。
Step 2:把原文错误(含文件名:行号)整段贴回 AI
不要复述、不要总结。原文越完整,AI 修得越准:
我跑 tsc --noEmit 得到以下错误,请按以下要求修:
1. 不准用 as any / as unknown as
2. 不准为了通过加 // @ts-ignore / // @ts-expect-error
3. 如果某个第三方库的类型确实有问题,告诉我该升级哪个版本或装哪个 @types/*
4. 修完贴一段 diff,并解释每个改动的原因
错误日志:
src/api/user.ts:34:7 - error TS2322: Type 'string' is not assignable to type 'number'.
src/api/user.ts:41:12 - error TS2339: Property 'email' does not exist on type 'unknown'.
[完整粘贴]
Step 3:让 AI 在循环里自我验证(agent loop)
Claude Code / Cursor Composer / Aider 都支持运行命令。配置成”改完代码必须跑 tsc”:
你要在本任务里:
1. 修复类型错误
2. 每次改完执行 `npx tsc --noEmit`
3. 如果还有错,继续修,最多 5 轮
4. 5 轮后还修不完,停下来告诉我哪些错你不会修,列出原文
Aider 直接加 --auto-test --test-cmd "npx tsc --noEmit"。
Step 4:第三方库类型不对就找 @types/* 或上游 .d.ts
很多 npm 包类型由社区维护:
npm install -D @types/node @types/react @types/lodash
# 看看库自己有没有打包类型
npm view <pkg> types
# 没 @types 也没自带就让 AI 写个最小 declaration
echo 'declare module "untyped-lib";' > src/types/untyped-lib.d.ts
避免 as any——临时声明 module 至少未来可逐步补全。
Step 5:常见错误码 → 修复方式对照表
| 错误码 | 含义 | 修复方向 |
|---|---|---|
| TS2322 | 类型不匹配 | 检查赋值两端类型,加显式 type 注解 |
| TS2339 | 属性不存在 | type 太宽,需要类型守卫 / in 检查 / 断言 |
| TS2345 | 参数类型不对 | 看函数签名,确认调用方传值类型 |
| TS2554 | 参数数量不对 | 库版本变了,查 changelog |
| TS2531/2532 | null/undefined 未处理 | 加 if (x) 守卫 / x?.foo / x! |
| TS7006 | 隐式 any | 给参数显式类型 |
| TS2304 | 找不到名字 | import 漏了 / @types/* 没装 |
| TS1484 | type-only import | 改成 import type |
| TS2741 | 缺必需属性 | 检查对象字面量,补齐字段 |
预防建议
- 把
tsc --noEmit放进 AI 的 agent loop / pre-commit hook,类型不过直接拦住 CLAUDE.md/.cursorrules写死:禁止as any、禁止@ts-ignore、禁止@ts-expect-error不带原因注释tsconfig.json开"strict": true+"noUncheckedIndexedAccess": true,让 AI 必须显式处理 null- code review 时搜
as any/// @ts-,零容忍 - 升级第三方依赖时让 AI 同步检查
.d.ts是否需要换@types/*版本 - 使用
tsc --noEmit --extendedDiagnostics分析 type 性能,避免 AI 写出递归 union 拖垮编译