Claude Code 生成 TypeScript 代码后,如何用 git diff 与 tsc 做好验收
1. 一个需求做完之后我才意识到验收才是真正的深水区用 Claude Code 写完一个 TypeScript 需求代码跑通了测试也过了我原本以为这事就算结了。结果在提交前随手跑了一次git diff --check屏幕上冒出来一堆尾随空格和行尾不一致的警告紧接着 CI 上又因为类型定义冲突挂了。那一刻我才真正理解一件事AI 帮你写代码快的是生成慢的是验收。prompt 写得好不好顶多决定它第一版给你什么验收做得够不够细才决定这份代码能不能真正进主干、能不能扛住后面几个月的迭代。这篇内容我想聊的就是这个被大多数人低估的环节。它适合两类人一类是刚开始用 Claude Code、TypeScript 做日常开发觉得AI 写完就能用的朋友另一类是团队里负责代码评审、需要把 AI 产出纳入正式流程的人。我会把整个验收链路拆开讲——从 diff 审查、类型检查、边界验证到验收资料怎么留痕尽量给到可以直接抄作业的步骤和参数。核心关键词就几个prompt、验收、Claude Code、TypeScript、git diff它们会贯穿全文。先说结论AI 生成代码的验收和传统人工写代码的验收最大的区别在于信任基线不同。人写的代码你默认他理解业务AI 写的代码你必须默认它只理解了字面业务语义、边界条件、历史包袱它一概不知。所以验收的重点要从代码对不对前移到它到底理解了什么。2. 为什么 AI 写完的代码验收逻辑要重新设计2.1 prompt 决定下限验收决定上限很多人把精力全砸在 prompt engineering 上研究怎么把提示词写得又长又准。这没错但有个认知误区prompt 只能约束 AI 的输出方向约束不了输出的正确性。你写请实现一个用户登录接口返回 JWT它可能给你一个能跑的版本但 token 过期时间、刷新逻辑、异常分支、并发场景这些它未必按你的预期处理。我做过一个对比实验。同一个需求用两版 prompt 让 Claude Code 生成 TypeScript 代码第一版 prompt 只写功能描述第二版 prompt 补充了类型约束、错误码规范、边界条件。结果第一版代码能跑但类型全是any第二版类型完整但多了一个我没要求的缓存层。你看prompt 优化了问题并没有消失只是换了个形式。真正兜住质量的是后面那套验收动作。所以我的经验是prompt 投入产出比在 60 分到 85 分之间最高剩下 15 分必须靠验收补。指望 prompt 写到 100 分边际成本高得离谱而且不稳定。2.2 AI 代码的三个典型信任陷阱在讲具体验收方法前先说说 AI 生成代码最容易埋的坑这些是我踩过多次总结出来的类型幻觉TypeScript 项目里AI 经常写出看起来类型正确的代码。比如它声明返回PromiseUser但实际某个分支返回了null却没在类型里体现。编译器不报错运行时才炸。边界缺失空数组、超长字符串、并发调用、网络超时这些边界 AI 默认不处理除非你明确要求。它倾向于写happy path。历史包袱无视项目里已有的工具函数、约定俗成的错误处理方式、特定的目录结构AI 不知道也不会主动遵守它按自己的理解重新造轮子。这三个陷阱决定了验收不能只看能不能跑必须有一套结构化的检查清单。2.3 验收的本质是重建信任我习惯把 AI 代码验收理解成一次信任重建过程。AI 生成时我对它零信任通过一层层检查逐步把信任加回来。每一层检查对应一类风险检查层对应风险主要工具差异审查改动了不该改的地方git diff格式规范尾随空格、行尾、缩进git diff --check类型检查类型幻觉、隐式 anytsc --noEmit逻辑验证边界缺失、分支遗漏单元测试、手动用例集成验证与现有代码冲突全量构建、回归测试这张表是我实际验收时的顺序从便宜到贵从快到慢。前面几层几分钟就能过后面几层可能要跑完整测试套件。顺序不能乱因为便宜的检查能过滤掉大量低级问题避免你在昂贵的检查上浪费时间。3. 验收第一关用 git diff 把 AI 的改动看穿3.1 为什么 diff 审查是验收的起点Claude Code 这类工具工作时往往一次性改动多个文件。它可能重构了一个工具函数、调整了类型定义、顺手改了配置文件。如果你不先看 diff 就直接跑测试很可能测试过了但引入了你根本没意识到的副作用。git diff是验收的第一道闸门。我的习惯是 AI 生成完代码后第一件事不是运行而是git diff逐文件过一遍。重点看三类改动预期内的改动需求直接涉及的文件正常。预期外的改动需求没提但被改的文件必须搞清楚为什么。删除的代码AI 有时会顺手清理它认为没用的代码这最危险。3.2 git diff --check 到底在检查什么热词里出现了git diff --check的作用这个命令很多人知道但说不清。它的作用是检查即将提交的改动里有没有空白字符问题具体包括行尾多余的空格trailing whitespace行尾的空白行文件末尾缺少换行行尾符不一致CRLF vs LF为什么这个对 AI 代码特别重要因为 AI 生成代码时对空白字符不敏感它可能给你一堆行尾空格或者把 LF 文件改成 CRLF。这些在本地看不出来但一旦进 CI轻则警告重则因为 lint 规则直接失败。# 检查工作区改动 git diff --check # 检查已暂存的改动 git diff --cached --check # 检查某个提交 git diff HEAD~1 HEAD --check实测下来AI 生成的代码里git diff --check报错的概率比人工代码高不少。我现在的流程是AI 生成完先git diff --check有问题先修再进入下一步。这一步花不了 30 秒但能省掉后面 CI 挂掉重新排查的半小时。3.3 差异审查的实操清单光看 diff 还不够得有清单。我整理了一份自己常用的 diff 审查清单有没有改动package.json、tsconfig.json这类配置文件改了要确认依赖版本和编译选项没被意外调整。有没有新增依赖新增的依赖是否必要版本是否合理。有没有删除已有函数或导出删了要确认没有其他地方引用。类型定义有没有被放宽比如string变成anyUser变成unknown。有没有引入新的全局变量或副作用提示审查 diff 时建议用git diff --stat先看改动概览再逐个文件细看。改动文件超过 10 个时优先看配置文件和类型定义文件它们的影响面最大。4. TypeScript 项目的类型验收别被能编译骗了4.1 tsc --noEmit 是底线不是全部TypeScript 项目验收第一反应是跑tsc --noEmit。这没错但要知道它的局限它只能验证类型系统内部的一致性验证不了类型和运行时行为是否匹配。# 基础类型检查 npx tsc --noEmit # 严格模式检查推荐 npx tsc --noEmit --strict # 检查未使用的变量和参数 npx tsc --noEmit --noUnusedLocals --noUnusedParametersAI 生成的代码经常能通过tsc --noEmit但存在类型幻觉。比如它写async function getUser(id: string): PromiseUser { const res await fetch(/api/user/${id}); const data await res.json(); return data; // data 是 any被隐式断言成 User }这段代码类型检查能过因为res.json()返回anyany可以赋值给任何类型。但运行时如果接口返回错误结构data根本不是User调用方拿到的就是脏数据。类型检查过了不代表类型是对的。4.2 类型验收的三个深水区我在 TypeScript 项目里验收 AI 代码重点盯三个地方第一隐式 any 和类型断言。搜索代码里有没有as断言、有没有any、有没有ts-ignore。AI 为了让代码编译通过很爱用这些手段。每一个as都要问这里为什么需要断言能不能用类型守卫替代第二可选链和空值处理。AI 经常写user?.name但后面又直接user.name逻辑不一致。或者声明返回User | null但调用方没处理null。第三泛型和类型工具的使用。热词里提到vue 类型工具与现有 typescript 7 不兼容、typescript 命名空间 declare global这些是真实项目里常见的类型冲突点。AI 生成泛型代码时经常写出约束过宽或过窄的类型导致和其他模块不兼容。4.3 类型验收的实操方法我的做法是分三步静态扫描用 grep 或 IDE 搜索any、as、ts-ignore、ts-expect-error逐个审查。严格模式编译确保tsconfig.json里strict: true然后tsc --noEmit。类型测试对关键类型写类型测试用expectType之类的工具验证类型推导结果。// 类型测试示例 import { expectType } from tsd; // 验证 getUser 返回类型 expectTypePromiseUser(getUser(123)); // 验证错误分支类型 expectTypePromiseUser | null(getUserSafe(123));这一步很多人跳过但对 AI 代码特别值得做。因为 AI 的类型推导经常和你的预期有偏差类型测试能把这个偏差显性化。注意如果项目里用了declare global扩展全局类型AI 生成的代码可能和现有全局声明冲突。验收时务必检查global.d.ts或类似文件有没有被改动。5. 逻辑与边界验收AI 最不擅长的地方5.1 为什么边界条件是 AI 的盲区AI 生成代码时训练数据里正常路径的代码远多于边界处理的代码。所以它默认写 happy path边界条件要么不写要么写得敷衍。我见过 AI 写的分页逻辑完全没处理pageSize为 0 或负数的情况也见过它写的数组处理没考虑空数组。验收逻辑时我的核心方法是主动构造边界用例而不是等测试报错。具体来说针对每个函数问自己输入为空、为 null、为 undefined 时会怎样输入超长、超大时会怎样并发调用时会怎样依赖的外部服务失败时会怎样5.2 单元测试是验收的放大器AI 生成代码后我通常会让它同时生成单元测试但不会直接信任这些测试。原因很简单AI 写的测试往往只覆盖它自己写的逻辑边界用例它想不到你也就测不到。我的做法是AI 生成测试作为基础然后我手动补充边界用例。补充的重点就是上面那四类问题。补充完之后跑覆盖率看哪些分支没被覆盖。# 运行测试并生成覆盖率报告 npx jest --coverage # 只跑改动的文件相关测试 npx jest --findRelatedTests src/user.ts覆盖率不是越高越好但关键路径的分支覆盖率必须看。AI 代码里那些if/else的 else 分支经常是空的或者直接 throw这些地方就是风险点。5.3 集成验收别让 AI 代码成为孤岛单个函数验收通过不代表集成没问题。AI 生成的代码可能用了和项目不一致的错误处理方式引入了循环依赖改了共享类型导致其他模块编译失败新增的依赖和现有依赖版本冲突集成验收我一般跑三件事全量构建、全量测试、关键路径手动验证。全量构建能发现类型和依赖问题全量测试能发现回归手动验证能发现那些测试覆盖不到的交互问题。验收类型命令示例发现问题类型全量构建npm run build类型冲突、依赖缺失全量测试npm test回归、集成失败手动验证启动应用走关键流程交互、体验问题6. 验收资料留痕让验收过程可追溯6.1 为什么要留验收资料热词里出现了服务器安装记录 记录验收资料、项目验收系统概要设计说明书说明验收留痕是个真实需求。对 AI 代码来说留痕尤其重要因为你需要证明这段代码经过了人工审查而不是直接信任 AI 输出。留痕的内容包括diff 审查记录、类型检查结果、测试覆盖率、手动验证步骤和结果。这些资料在代码评审、问题回溯、责任界定时都用得上。6.2 验收资料的实操模板我习惯在 PR 描述里附一份验收清单格式大概是这样## AI 代码验收记录 ### 生成信息 - 工具Claude Code - prompt 摘要实现用户登录接口返回 JWT - 生成时间2024-XX-XX ### 验收检查 - [x] git diff --check 通过 - [x] tsc --noEmit --strict 通过 - [x] 单元测试通过覆盖率 85% - [x] 手动验证登录、登出、token 过期场景 - [x] 无新增未审查依赖 ### 遗留问题 - token 刷新逻辑待补充 - 并发登录场景未覆盖这份记录看起来繁琐但实际写起来五分钟能省掉后面扯皮的两小时。而且它逼着你把验收做完整不能糊弄。6.3 把验收清单固化到流程里个人开发靠自觉团队开发靠流程。我建议把 AI 代码验收清单固化到 CI 里能自动化的自动化git diff --check放进 pre-commit hooktsc --noEmit放进 CI测试覆盖率阈值卡在 CI 里PR 模板里加验收清单勾选项这样即使有人想偷懒流程也会拦住。验收不是靠人记得做而是靠流程保证必须做。7. 常见问题与排查技巧实录7.1 AI 代码验收常见问题速查问题现象可能原因排查方法CI 报尾随空格AI 生成时未清理空白git diff --check类型检查过但运行报错类型幻觉、隐式 any搜索as、any测试过了但线上出问题边界未覆盖补充边界用例改动文件超出预期AI 顺手重构git diff --stat审查依赖版本冲突AI 新增依赖检查package.json全局类型冲突declare global被改检查.d.ts文件7.2 几个我踩过的坑坑一信任 AI 写的测试。早期我让 AI 生成代码和测试测试全绿就提交。结果线上发现一个空数组导致的崩溃而 AI 的测试里根本没测空数组。教训AI 的测试只能作为起点边界用例必须自己补。坑二忽略 git diff --check。有次提交后 CI 挂了排查半天发现是行尾符问题。AI 生成的文件用了 CRLF项目要求 LF。从那以后git diff --check成了我的固定动作。坑三类型断言埋雷。AI 写了个data as User当时类型检查过了我没细看。后来接口返回结构变了data根本不是User但类型系统没报错运行时才炸。教训每一个as都要问为什么。坑四prompt 太长导致截断。热词里有prompt is too long我也遇到过。prompt 写太长AI 反而抓不住重点生成质量下降。后来我学会把需求拆成小块分步生成每步验收。7.3 验收效率的提升技巧验收虽然重要但不能无限投入。我的效率技巧分层验收便宜的检查先跑贵的检查后跑快速过滤。自动化优先能进 CI 的绝不手动。重点盯类型和边界这两块是 AI 代码的重灾区。建立个人检查清单把踩过的坑变成清单项下次直接对照。提示验收时间控制在生成时间的 1 到 2 倍比较合理。如果验收时间远超生成时间说明 prompt 或需求拆分有问题要回头优化。8. 我个人的一点体会用 Claude Code 这类工具做 TypeScript 开发最大的认知转变是AI 把写代码的成本降下来了但把验收的成本顶上去了。以前写代码慢但写的时候脑子里就在做验收现在生成快验收变成了一个独立的、必须刻意执行的环节。我现在的习惯是AI 生成完代码先不急着高兴先跑git diff --check再看 diff再跑类型检查再补边界测试。这一套下来一个中等需求的验收大概二十分钟。听起来不少但比起线上出问题再回滚排查这二十分钟花得值。最后分享一个小技巧把每次验收发现的问题记下来攒成自己的AI 代码避坑清单。用不了多久你就会发现AI 犯的错其实就那么几类清单越来越短验收越来越快。验收这件事本质上是在和 AI 的平均水准博弈你越了解它的短板验收就越有的放矢。