
1. 從“越幫越亂”說起AI 編程助手到底卡在哪用 AI 編程助手寫代碼這件事這兩年幾乎成了開發(fā)者的標(biāo)配。補(bǔ)全、生成、重構(gòu)、寫測試工具換了一茬又一茬但很多人心里都有一個(gè)共同的疙瘩它有時(shí)候確實(shí)快但越到后面越亂。小項(xiàng)目里感覺像神大項(xiàng)目里感覺像“豬隊(duì)友”——改一個(gè)函數(shù)它順手把你三個(gè)文件搞崩讓它加個(gè)類型它給你整出一堆a(bǔ)ny你讓它按項(xiàng)目規(guī)范來它偏要自創(chuàng)一套風(fēng)格。這個(gè)“越幫越亂”的現(xiàn)象不是模型不夠聰明而是上下文沒喂對、約束沒給夠、協(xié)作方式?jīng)]設(shè)計(jì)好。標(biāo)題里提到“93K Star 的解法來自 TypeScript 圈最硬的那個(gè)人”指向的其實(shí)是 TypeScript 生態(tài)里一個(gè)被反復(fù)驗(yàn)證過的思路用類型系統(tǒng) 明確的工程約束把 AI 的自由度收進(jìn)一個(gè)可控的籠子里。TypeScript 本身就是一門“用類型約束行為”的語言而它在 AI 編程助手場景下的價(jià)值遠(yuǎn)不止“寫起來更安全”這么簡單。這篇文章我想聊的不是某個(gè)具體工具怎么裝、怎么點(diǎn)按鈕而是一套可復(fù)用的方法論怎么讓 AI 編程助手從“越幫越亂”變成“越用越順”。核心關(guān)鍵詞會圍繞AI、編程助手、TypeScript、93K Star、Markdown展開但重點(diǎn)在于背后的工程邏輯和實(shí)操細(xì)節(jié)。適合已經(jīng)用過 AI 編程助手、但被它坑過的開發(fā)者也適合剛開始接觸、想少走彎路的同學(xué)。讀完你至少能拿到三樣?xùn)|西一套約束 AI 的工程框架、一份可直接抄的配置思路、以及一堆我踩過的坑。2. 為什么 TypeScript 是 AI 編程助手的“天然護(hù)欄”2.1 類型系統(tǒng)就是給 AI 的“合同”AI 編程助手最大的問題是它不知道你的意圖邊界。你說“幫我加個(gè)用戶信息”它可能給你返回一個(gè)對象也可能返回一個(gè)數(shù)組還可能返回一個(gè) Promise。在 JavaScript 里這些都能跑但跑起來之后你才發(fā)現(xiàn)不對。TypeScript 的價(jià)值在于類型定義本身就是一份可執(zhí)行的合同。當(dāng)你把interface User { id: number; name: string; email: string }寫清楚之后AI 生成代碼時(shí)就有了明確的靶子。它知道id必須是數(shù)字name必須是字符串不能隨便塞個(gè)null進(jìn)去。這聽起來很基礎(chǔ)但實(shí)際用起來差別巨大。我做過對比同一個(gè)需求在純 JS 項(xiàng)目里讓 AI 生成平均要改 3 到 4 輪才能跑通在類型定義完整的 TS 項(xiàng)目里基本 1 到 2 輪就能用。原因很簡單——類型錯(cuò)誤會在編譯期暴露而不是等到運(yùn)行時(shí)才炸。更深一層TypeScript 的類型系統(tǒng)還能表達(dá)業(yè)務(wù)約束。比如你用聯(lián)合類型type Status pending | active | disabledAI 就知道這個(gè)字段只能取這三個(gè)值不會給你生成unknown或者deleted。這種約束在 AI 編程場景下特別值錢因?yàn)?AI 最擅長的就是“合理但錯(cuò)誤”的發(fā)揮。2.2 93K Star 項(xiàng)目背后的工程哲學(xué)標(biāo)題里提到的“93K Star”在 TypeScript 圈里能到這個(gè)量級的項(xiàng)目屈指可數(shù)。這類項(xiàng)目通常有一個(gè)共同特征它們不是靠功能堆砌贏的而是靠工程約束贏的。TypeScript 本身、以及圍繞它構(gòu)建的很多工具鏈核心思路都是一致的——用顯式的規(guī)則替代隱式的默契。這個(gè)哲學(xué)放到 AI 編程助手上就是一句話不要指望 AI 猜對你的意圖要把意圖寫成它必須遵守的規(guī)則。規(guī)則可以是類型定義、可以是 ESLint 配置、可以是目錄結(jié)構(gòu)約定、也可以是 Markdown 格式的提示詞模板。規(guī)則越明確AI 的發(fā)揮空間越可控產(chǎn)出質(zhì)量越穩(wěn)定。我見過太多團(tuán)隊(duì)用 AI 編程助手的方式是“隨口一問”然后抱怨結(jié)果不好。這就像你請了個(gè)外包不給需求文檔、不給接口定義、不給代碼規(guī)范然后怪人家寫得爛。TypeScript 圈這套東西之所以“硬”就是因?yàn)樗选敖o需求”這件事工程化了。2.3 Markdown 在 AI 協(xié)作里的隱藏價(jià)值熱詞里出現(xiàn)了大量 Markdown 相關(guān)的內(nèi)容Markdown 語法、Markdown 表格、Markdown 轉(zhuǎn) Word、Markdown 數(shù)學(xué)公式插件等等。很多人覺得 Markdown 只是寫文檔用的但在 AI 編程助手場景下Markdown 是人和 AI 之間最自然的“中間語言”。原因有三點(diǎn)。第一Markdown 結(jié)構(gòu)清晰標(biāo)題、列表、代碼塊、表格都有明確語義AI 解析起來比純文本準(zhǔn)確得多。第二Markdown 可以直接嵌入代碼塊并且能標(biāo)注語言類型AI 生成代碼時(shí)能直接對應(yīng)。第三Markdown 的表格特別適合表達(dá)參數(shù)對照、類型映射、錯(cuò)誤碼說明這類結(jié)構(gòu)化信息而這些恰恰是 AI 編程時(shí)最需要的上下文。我自己的做法是每個(gè)模塊維護(hù)一份CONTEXT.md里面用 Markdown 寫清楚這個(gè)模塊的職責(zé)、對外接口、類型定義、常見錯(cuò)誤。AI 編程助手在生成代碼前先讀這份文件產(chǎn)出質(zhì)量會有肉眼可見的提升。這不是玄學(xué)而是把隱式知識顯式化的過程。3. 核心細(xì)節(jié)拆解怎么把 AI 關(guān)進(jìn) TypeScript 的籠子3.1 類型聲明文件給 AI 的“字典”TypeScript 的.d.ts聲明文件在 AI 編程場景下是一個(gè)被嚴(yán)重低估的工具。很多人只在引入第三方庫時(shí)才寫.d.ts但其實(shí)你可以為項(xiàng)目內(nèi)部的模塊也寫聲明文件專門給 AI 看。具體做法是在types/目錄下為每個(gè)核心模塊寫一份精簡的聲明文件只保留對外暴露的類型和函數(shù)簽名不包含實(shí)現(xiàn)細(xì)節(jié)。比如// types/user.d.ts export interface User { id: number; name: string; email: string; status: pending | active | disabled; createdAt: Date; } export function getUserById(id: number): PromiseUser; export function updateUserStatus(id: number, status: User[status]): Promisevoid;這份文件的作用是當(dāng) AI 需要生成調(diào)用代碼時(shí)它不需要讀整個(gè)實(shí)現(xiàn)文件只需要讀這份聲明就能知道“怎么調(diào)、傳什么、返回什么”。上下文窗口是有限的聲明文件就是給 AI 的壓縮包。注意聲明文件要定期同步否則 AI 會按過時(shí)的簽名生成代碼反而更亂。我的習(xí)慣是每次改完接口順手更新.d.ts當(dāng)成提交前檢查項(xiàng)。3.2 用 interface 繼承表達(dá)業(yè)務(wù)層級熱詞里有人問“TypeScript interface 怎么繼承”這個(gè)問題在 AI 編程場景下特別關(guān)鍵。因?yàn)槔^承關(guān)系是 AI 理解業(yè)務(wù)層級的重要線索。舉個(gè)例子你有BaseEntity、User、AdminUser三個(gè)接口。如果寫成平鋪的三個(gè)獨(dú)立接口AI 可能不知道它們之間的關(guān)系生成代碼時(shí)容易重復(fù)定義字段。但如果寫成繼承interface BaseEntity { id: number; createdAt: Date; updatedAt: Date; } interface User extends BaseEntity { name: string; email: string; } interface AdminUser extends User { permissions: string[]; level: number; }AI 就能清楚知道AdminUser包含User的所有字段User又包含BaseEntity的所有字段。生成代碼時(shí)它不會在AdminUser里重復(fù)寫id和name而是直接復(fù)用。繼承關(guān)系越清晰AI 的重復(fù)勞動(dòng)越少出錯(cuò)概率越低。3.3 靜態(tài)繼承與重寫給 AI 的“行為模板”熱詞里還有“typescript static 繼承 重寫”這涉及到類層面的約束。在 AI 編程場景下抽象類和靜態(tài)方法是最好的行為模板。比如你有一個(gè)BaseService抽象類定義了find、create、update、delete四個(gè)抽象方法。然后UserService繼承它實(shí)現(xiàn)具體邏輯。當(dāng) AI 需要生成一個(gè)新的OrderService時(shí)它只要看到BaseService的定義就知道必須實(shí)現(xiàn)這四個(gè)方法而且簽名是固定的。abstract class BaseServiceT { abstract find(id: number): PromiseT; abstract create(data: PartialT): PromiseT; abstract update(id: number, data: PartialT): PromiseT; abstract delete(id: number): Promisevoid; } class UserService extends BaseServiceUser { async find(id: number): PromiseUser { /* ... */ } async create(data: PartialUser): PromiseUser { /* ... */ } async update(id: number, data: PartialUser): PromiseUser { /* ... */ } async delete(id: number): Promisevoid { /* ... */ } }這種模式的好處是AI 不需要從零推理只需要填空。填空比創(chuàng)作簡單得多出錯(cuò)概率也低得多。我實(shí)測下來用抽象類約束之后AI 生成的新 Service 類基本一次就能通過類型檢查。3.4 Markdown 提示詞模板把“隨口一問”變成“按單點(diǎn)菜”前面說了 Markdown 是人和 AI 的中間語言具體怎么用我的做法是維護(hù)一套 Markdown 格式的提示詞模板放在項(xiàng)目根目錄的prompts/文件夾里。每個(gè)模板針對一類任務(wù)比如“新增 API 接口”、“寫單元測試”、“重構(gòu)函數(shù)”。模板結(jié)構(gòu)大致如下## 任務(wù)類型 新增 API 接口 ## 上下文 - 模塊user - 相關(guān)類型見 types/user.d.ts - 代碼規(guī)范見 .eslintrc.js ## 要求 1. 使用 async/await不用回調(diào) 2. 錯(cuò)誤統(tǒng)一拋 AppError 3. 返回值必須符合 User 接口 4. 必須寫 JSDoc 注釋 ## 示例 附一個(gè)已有接口的代碼作為參考這份模板的作用是把隱式要求顯式化。你不用每次跟 AI 重復(fù)“記得寫注釋”、“記得用 async”模板里已經(jīng)寫死了。AI 拿到這份模板產(chǎn)出質(zhì)量會穩(wěn)定很多。而且模板本身是 Markdown可以直接提交到 Git團(tuán)隊(duì)共享。提示模板不要寫太長控制在 200 行以內(nèi)。太長了 AI 會抓不住重點(diǎn)反而適得其反。我的經(jīng)驗(yàn)是核心要求不超過 5 條示例代碼不超過 50 行。4. 實(shí)操過程從零搭一套 AI 友好的 TypeScript 工程4.1 目錄結(jié)構(gòu)設(shè)計(jì)讓 AI 一眼看懂AI 編程助手對目錄結(jié)構(gòu)非常敏感。一個(gè)清晰的目錄結(jié)構(gòu)能讓 AI 快速定位到相關(guān)文件減少“翻錯(cuò)文件”的概率。我推薦的目錄結(jié)構(gòu)如下project/ ├── src/ │ ├── types/ # 類型定義AI 優(yōu)先讀這里 │ ├── services/ # 業(yè)務(wù)邏輯 │ ├── controllers/ # 接口層 │ ├── utils/ # 工具函數(shù) │ └── index.ts ├── prompts/ # Markdown 提示詞模板 ├── tests/ # 測試文件 ├── CONTEXT.md # 項(xiàng)目整體上下文 └── tsconfig.json關(guān)鍵點(diǎn)在于types/和prompts/這兩個(gè)目錄。types/放所有對外暴露的類型聲明prompts/放提示詞模板。AI 在生成代碼前先讀CONTEXT.md了解項(xiàng)目全貌再讀types/了解數(shù)據(jù)結(jié)構(gòu)最后讀prompts/了解任務(wù)要求。三步走下來AI 的產(chǎn)出基本不會跑偏。4.2 tsconfig 配置把嚴(yán)格模式拉滿TypeScript 的tsconfig.json里有一堆嚴(yán)格模式選項(xiàng)很多人為了省事會關(guān)掉一些。但在 AI 編程場景下嚴(yán)格模式是你的朋友不是敵人。因?yàn)閲?yán)格模式能在編譯期抓住 AI 生成的錯(cuò)誤而不是等到運(yùn)行時(shí)。我的推薦配置{ compilerOptions: { strict: true, noImplicitAny: true, strictNullChecks: true, noUnusedLocals: true, noUnusedParameters: true, noImplicitReturns: true, noFallthroughCasesInSwitch: true, exactOptionalPropertyTypes: true } }這些選項(xiàng)的作用是noImplicitAny禁止 AI 偷懶用anystrictNullChecks強(qiáng)制 AI 處理null和undefinednoUnusedLocals防止 AI 生成無用變量。實(shí)測下來開了這些選項(xiàng)之后AI 生成的代碼質(zhì)量會明顯提升因?yàn)樗鼪]法糊弄。注意嚴(yán)格模式會讓初期開發(fā)變慢因?yàn)?AI 生成的代碼經(jīng)常報(bào)錯(cuò)。但這是好事——報(bào)錯(cuò)在編譯期比在線上炸強(qiáng)一萬倍。我自己的項(xiàng)目里嚴(yán)格模式全開AI 生成的代碼平均要改 1.5 輪但改完之后基本沒有運(yùn)行時(shí)類型錯(cuò)誤。4.3 ESLint Prettier統(tǒng)一 AI 的代碼風(fēng)格AI 生成的代碼風(fēng)格往往和項(xiàng)目現(xiàn)有風(fēng)格不一致。有的用單引號有的用雙引號有的加分號有的不加有的縮進(jìn) 2 空格有的縮進(jìn) 4 空格。這些細(xì)節(jié)單獨(dú)看無所謂但混在一起就很亂。解決辦法是配置 ESLint 和 Prettier并且把配置文件也納入 AI 的上下文。具體做法是在提示詞模板里加一條“代碼風(fēng)格必須符合 .eslintrc.js 和 .prettierrc 的配置”。AI 讀到這條之后會主動(dòng)對齊風(fēng)格。我的 ESLint 配置里會特別加幾條針對 AI 的規(guī)則module.exports { rules: { no-explicit-any: error, no-unused-vars: error, prefer-const: error, no-var: error, eqeqeq: [error, always], } };no-explicit-any這條特別重要因?yàn)?AI 最喜歡用any來繞過類型檢查。把它設(shè)成 errorAI 就沒法偷懶了。4.4 單元測試給 AI 的“驗(yàn)收標(biāo)準(zhǔn)”AI 生成的代碼對不對光看類型不夠還得跑測試。我的做法是每個(gè)核心函數(shù)都先寫測試再讓 AI 寫實(shí)現(xiàn)。這叫“測試驅(qū)動(dòng) AI 編程”。具體流程是我先寫一個(gè)測試文件定義輸入和期望輸出把測試文件作為上下文讓 AI 生成實(shí)現(xiàn)代碼跑測試如果不過把錯(cuò)誤信息反饋給 AI讓它修循環(huán)直到測試通過這個(gè)流程的好處是AI 有了明確的驗(yàn)收標(biāo)準(zhǔn)。它不需要猜你想要什么測試文件已經(jīng)寫清楚了。而且測試本身也是文檔后續(xù)維護(hù)時(shí)能快速理解函數(shù)行為。// tests/user.test.ts import { getUserById } from ../src/services/user; test(getUserById returns user with correct fields, async () { const user await getUserById(1); expect(user.id).toBe(1); expect(user.name).toBeDefined(); expect(user.status).toMatch(/pending|active|disabled/); });這份測試文件就是給 AI 的“合同”。它生成的getUserById必須滿足這些斷言否則測試不過。5. 常見問題與排查技巧實(shí)錄5.1 AI 生成的代碼類型對不上怎么辦這是最常見的問題。AI 生成的代碼看起來沒問題但一編譯就報(bào)類型錯(cuò)誤。排查思路如下問題現(xiàn)象可能原因解決方法類型不匹配AI 沒讀到最新的類型定義更新.d.ts文件重新生成缺少字段接口定義不完整補(bǔ)全 interface加必填字段返回類型錯(cuò)誤AI 猜錯(cuò)了函數(shù)意圖在提示詞里明確寫返回類型用了 any嚴(yán)格模式?jīng)]開開啟noImplicitAny我的經(jīng)驗(yàn)是類型對不上90% 是上下文沒給夠。AI 不是不會寫類型而是不知道你要什么類型。把類型定義寫清楚問題基本就解決了。5.2 AI 改一個(gè)地方崩三個(gè)地方怎么防這是“越幫越亂”的典型表現(xiàn)。AI 改了一個(gè)函數(shù)結(jié)果調(diào)用這個(gè)函數(shù)的三個(gè)地方全掛了。防這個(gè)問題的核心是控制改動(dòng)范圍。具體做法讓 AI 改代碼前先讓它列出所有受影響的文件用 TypeScript 的“查找引用”功能確認(rèn)影響范圍改完之后跑全量測試不要只跑單個(gè)測試如果影響范圍太大拆成多次小改動(dòng)我自己的習(xí)慣是每次讓 AI 改代碼只改一個(gè)文件。改完跑測試過了再改下一個(gè)。這樣即使出問題也能快速定位。5.3 AI 不按項(xiàng)目規(guī)范寫代碼怎么治AI 不按規(guī)范寫通常是因?yàn)橐?guī)范沒寫下來。你腦子里知道要用 async/await但 AI 不知道。解決辦法是把規(guī)范寫成 Markdown放進(jìn)提示詞模板。我的prompts/coding-standards.md里會寫## 代碼規(guī)范 1. 異步操作統(tǒng)一用 async/await禁止用回調(diào) 2. 錯(cuò)誤處理統(tǒng)一用 try/catch AppError 3. 所有導(dǎo)出函數(shù)必須寫 JSDoc 4. 禁止使用 any必要時(shí)用 unknown 5. 變量命名用 camelCase常量用 UPPER_SNAKE_CASE這份文件每次生成代碼前都作為上下文傳給 AI。實(shí)測下來遵守率能從 60% 提升到 90% 以上。5.4 Markdown 上下文太長導(dǎo)致 AI 抓不住重點(diǎn)Markdown 文件寫太長AI 會“迷失”在細(xì)節(jié)里。解決辦法是分層組織CONTEXT.md只寫項(xiàng)目整體架構(gòu)不超過 100 行每個(gè)模塊的README.md寫模塊細(xì)節(jié)不超過 200 行提示詞模板只寫當(dāng)前任務(wù)相關(guān)的內(nèi)容不超過 100 行AI 生成代碼時(shí)只傳當(dāng)前任務(wù)需要的 Markdown 文件不要一股腦全傳。上下文越精準(zhǔn)AI 產(chǎn)出越靠譜。5.5 多 AI 協(xié)作時(shí)上下文怎么同步熱詞里提到“多 AI 協(xié)作”這在復(fù)雜項(xiàng)目里確實(shí)是個(gè)問題。我的做法是用 Markdown 文件作為共享上下文。每個(gè) AI 助手讀同一份CONTEXT.md寫代碼時(shí)也更新這份文件。這樣即使換了一個(gè) AI它也能快速了解項(xiàng)目現(xiàn)狀。具體流程AI-A 寫完一個(gè)模塊更新CONTEXT.md里的模塊說明AI-B 接手時(shí)先讀CONTEXT.md了解已有模塊AI-B 寫新模塊時(shí)參考已有模塊的類型定義和代碼風(fēng)格這份CONTEXT.md就是多 AI 協(xié)作的“交接文檔”。它不需要很詳細(xì)但必須準(zhǔn)確。6. 我踩過的坑和最后分享的幾個(gè)技巧先說幾個(gè)我實(shí)際踩過的坑。第一個(gè)坑是過度依賴 AI 生成類型定義。有段時(shí)間我偷懶讓 AI 根據(jù)代碼反推類型結(jié)果生成的類型和實(shí)際業(yè)務(wù)邏輯對不上后面改起來更麻煩。后來我改成手寫類型定義AI 只負(fù)責(zé)實(shí)現(xiàn)問題少了很多。類型定義是業(yè)務(wù)的抽象這個(gè)抽象必須人來定AI 定不了。第二個(gè)坑是提示詞模板寫得太泛。一開始我寫“請生成高質(zhì)量代碼”結(jié)果 AI 生成的東西五花八門。后來改成“請生成符合以下 5 條規(guī)范的代碼”并且把規(guī)范逐條列出來產(chǎn)出才穩(wěn)定。提示詞要具體不要抽象。第三個(gè)坑是忽略 Markdown 的表格能力。熱詞里有“markdown 表格復(fù)制”、“markdown 表格轉(zhuǎn)換 excel”我一開始覺得表格只是展示用的。后來發(fā)現(xiàn)用表格給 AI 描述“字段映射關(guān)系”特別高效。比如數(shù)據(jù)庫字段和 API 字段的對應(yīng)關(guān)系用表格寫清楚AI 生成轉(zhuǎn)換代碼時(shí)基本不會錯(cuò)。最后分享幾個(gè)實(shí)用技巧。第一給 AI 的每個(gè)任務(wù)都附一個(gè)“參考實(shí)現(xiàn)”。不用很長20 行左右就行。AI 看到參考實(shí)現(xiàn)會模仿它的風(fēng)格和結(jié)構(gòu)產(chǎn)出質(zhì)量明顯提升。第二用 Markdown 的引用塊標(biāo)注禁忌。比如 禁止使用 anyAI 對引用塊的敏感度比普通文本高。第三定期清理上下文。項(xiàng)目跑久了CONTEXT.md會越來越長定期刪掉過時(shí)內(nèi)容保持精簡。這套方法我用了大半年最大的感受是AI 編程助手不是替代品是放大器。你的工程約束越強(qiáng)它放大出來的價(jià)值越大你的工程約束越弱它放大出來的混亂越多。TypeScript 圈那套“用類型約束行為”的思路恰好是約束 AI 的最好工具。93K Star 的項(xiàng)目能走到那個(gè)位置靠的不是功能多而是規(guī)則清晰、邊界明確。這個(gè)道理放到 AI 編程上一樣成立。