你有沒有遇過這種狀況:請 AI 改個小功能,它改得亂七八糟,跑都跑不起來?
我以前也以為是 AI 太笨,後來才發現——是我寫得太亂。直到我用 TypeScript + 函數式設計重構,AI 才終於能幫上忙,連我自己 debug 也輕鬆多了。
TypeScript 能夠透過型別註解和純粹的小函數拆解,讓程式碼結構變得清晰且可預測,大幅提升 AI 理解與生成代碼的能力,也讓團隊協作和維護更加有效率。LLM 藉由 TypeScript 的型別資訊自動辨識資料流與函數結構,結合宣告式、無副作用的函數式風格,讓語意表達明確,進一步提升 AI 生成高品質代碼的能力。
📌 本文將帶你從一段常見錯誤寫法出發,透過 TypeScript 型別註解與函數式思維,轉化為 AI 更易理解、你也更易維護的現代程式設計範式。
為什麼 AI 看不懂你寫的程式?
你是不是也曾有過類似經驗:明明覺得自己寫得「很合理」,但 AI 卻無法正確補完、改寫、甚至回答你的提問?
問題往往不在於 AI 模型太笨,而是你的程式碼結構對 AI 來說太「不透明」——資料流混亂、副作用太多、邏輯分散在不同區塊,導致它無法清楚建立起「輸入 → 處理 → 輸出」的模型理解。
AI 生成程式碼時,最怕遇到不透明的狀態變動或隱藏的副作用——它無法確定資料何時、在哪裡被修改,也無法保證每次執行結果一致。這種不確定性讓 AI 難以安全重構邏輯或產生可預期的程式。函數式設計正好解決這個問題:它強調純函數、資料不可變和明確的資料流,讓 AI 能輕鬆理解每個步驟,生成更穩定、可重複的代碼。
來看一個常見例子:我們想計算所有用戶的訂單總額,同時記錄每筆訂單的處理過程。以下是典型寫法:
class OrderCalculator {
constructor(users) {
this.users = users;
}
getTotalAmount() {
let sum = 0;
for (const user of this.users) {
for (const order of user.orders) {
sum += order.amount;
}
}
return sum;
}
getOrderLogs() {
const logs = [];
for (const user of this.users) {
for (const order of user.orders) {
logs.push(`Added order ${order.id} (${order.amount})`);
}
}
return logs;
}
}
// 使用場景舉例
// 假設在一個後端服務中,我們需要根據資料庫查詢結果來計算所有用戶的總訂單金額,並記錄每筆訂單的累加過程。
const users = [
{ id: "u1", orders: [{ id: "o1", amount: 100 }] },
{ id: "u2", orders: [{ id: "o2", amount: 50 }, { id: "o3", amount: 75 }] }
];
const calculator = new OrderCalculator(users);
const total = calculator.getTotalAmount(); // 225
const log = calculator.getOrderLogs();
console.log(log); // ["Added order o1 (100)", "Added order o2 (50)", "Added order o3 (75)"]
console.log(total); // 225
這段程式碼雖然「能跑」,但對 AI 而言有幾個難以處理的特徵:
- 依賴類別內部狀態(this.users),使資料流不明確
- 多層巢狀邏輯與副作用(log 組裝與計算混在一起)
- 程式意圖需靠閱讀整段才能推論,不利於 AI 斷點理解與補完
- 這樣的寫法對 AI 來說,就像在看一個沒有章法的流程圖 —— 它可以「模糊理解」,但無法保證產出邏輯正確、可維護的代碼。
那如果換成 TypeScript 結合函數式設計呢?效果會是什麼樣?我們來比較看看。
type Order = { id: string; amount: number };
type User = { id: string; orders: Order[] };
const getUserOrders = (users: User[]): Order[] => users.flatMap(user => user.orders);
const sumOrders = (orders: Order[]): number => orders.reduce((sum, o) => sum + o.amount, 0);
const getTotalAmount = (users: User[]): number =>
sumOrders(getUserOrders(users));
// --- 使用範例 ---
const users: User[] = [
{ id: "u1", orders: [{ id: "o1", amount: 100 }] },
{ id: "u2", orders: [{ id: "o2", amount: 50 }, { id: "o3", amount: 75 }] }
];
const total: number = getTotalAmount(users); // 225
這段程式高度宣告式(declarative),清楚表達「先攤平成所有訂單,再累加金額」。資料流與變數型別明確,AI 能輕易辨識每個步驟的語意,生成與維護都更加簡單。這就是 TypeScript 型別與函數式拆解,如何幫助 LLM 更好地讀懂與生成程式碼的關鍵。
換句話說,清晰的型別、拆解的邏輯與純函數設計,讓 LLM 能聚焦於每個步驟的「意圖」而非流程細節,大幅提升程式碼協作與自動生成的品質與可靠性。
下面這組測試案例展示了 getTotalAmount
函數的各種情境,從單一使用者、多個使用者到空陣列,確保其正確累加所有訂單金額,並能處理沒有訂單或用戶的情況:
import { describe, it, expect } from "vitest";
describe("getTotalAmount", () => {
it("能正確加總單一使用者的所有訂單金額", () => {
const users: User[] = [
{ id: "a", orders: [{ id: "o1", amount: 20 }, { id: "o2", amount: 30 }] }
];
expect(getTotalAmount(users)).toBe(50);
});
it("能略過沒有訂單的使用者,只加總有訂單的金額", () => {
const users: User[] = [
{ id: "a", orders: [] },
{ id: "b", orders: [{ id: "o3", amount: 10 }] }
];
expect(getTotalAmount(users)).toBe(10);
});
it("能處理空的使用者陣列,回傳 0", () => {
expect(getTotalAmount([])).toBe(0);
});
});
這些測試有助於確保你的累加邏輯既簡潔又健壯,也能作為 AI 工具理解與推理的高品質樣板。
TypeScript:AI 協作的理想語言
TypeScript 是 JavaScript 的超集,為動態語言世界帶來了靜態型別、結構化程式設計和先進的型別推斷能力。這些特性不僅讓人類開發者寫出更安全、可維護的程式碼,也讓 LLM(大型語言模型)更容易理解、推理與生成高質量的代碼。
靜態型別如何幫助 AI 精準推理
TypeScript 的型別註解讓變數、參數和返回值都明確標註型別,大幅降低語意歧義。LLM 理解與生成邏輯時,可以根據型別約束自動排除錯誤推論:
function add(a: number, b: number): number {
return a + b;
}
// LLM 可從型別自動推導參數與回傳值,減少出錯
即使在複雜資料結構下,型別系統依然能提供強大的安全保障:
interface User {
id: string;
name: string;
email?: string;
}
function getUserName(user: User): string {
return user.name;
}
與 JavaScript 完美兼容,無縫接軌現有生態系
TypeScript 原生支援將代碼編譯為標準 JavaScript,開發團隊可無痛遷移、漸進升級。這讓 LLM 生成的 TypeScript 程式碼可以直接與現有 JS 專案融合,不必擔心生態斷層。舊有 JavaScript 檔案可逐步轉換,享受型別保障的同時維持生產力。
// TypeScript 支援多種模組系統與瀏覽器/Node.js 執行環境
export function toUpperCase(str: string): string {
return str.toUpperCase();
}
泛型與推斷:讓 AI 寫出可重用的好程式
TypeScript 支援泛型和強大的型別推斷,讓程式碼高度抽象且安全,LLM 能生成適用於多種情境的泛用邏輯,提升可重用性:
function mapArray<T, U>(arr: T[], fn: (item: T) => U): U[] {
return arr.map(fn);
}
const numbers = [1, 2, 3];
const strings = mapArray(numbers, n => n.toString());
除了函數泛型,TypeScript 也支援介面與類別泛型,讓複雜結構更安全:
interface ApiResponse<T> {
data: T;
error?: string;
}
提升協作與維護性
靜態型別與明確接口(interface/type)設計,讓多位開發者協作時規格一致、易於溝通,LLM 生成的程式碼可自動接受型別檢查,減少潛在錯誤並提升維護效率。例如:
type Point = { x: number; y: number };
function move(p: Point, dx: number, dy: number): Point {
return { x: p.x + dx, y: p.y + dy };
}
強大的 IDE 支援與自動補全
TypeScript 丰富的型別資訊使主流 IDE(如 VS Code)能提供即時型別提示、錯誤警告、重構工具與自動補全,不僅提升人類開發效率,也讓 LLM 生成更精準、上下文正確的程式片段。常見支援功能包括:
- 參數與返回型別即時提示
- 自動導入與補全
- 型別錯誤即時標註
- 智能重命名與重構
複雜篩選邏輯
在實際業務中,資料篩選往往不止是簡單的遍歷,而是涉及多層結構的累加、判斷與資料抽取。當你需要從一群用戶中找出「訂單總額超過 100」的用戶 id,這種邏輯在大型專案或 AI 輔助開發時尤其常見。命令式寫法與聲明式(函數式)寫法的差異,會直接影響程式碼的可讀性、可維護性,甚至影響 AI 理解與自動生成程式碼的能力。
命令式寫法:
const richUsers2: string[] = [];
for (const user of users) {
let sum = 0;
for (const order of user.orders) {
sum += order.amount;
}
if (sum > 100) {
richUsers2.push(user.id);
}
}
命令式寫法強調「每一步怎麼做」——你必須顯式建立暫存變數、手動累加、再檢查條件並 push 結果。每個細節都暴露給閱讀者,變數狀態容易混亂,維護時必須追蹤所有中間狀態與副作用。
函數式寫法:
const hasTotalOrderAmountOver100 = (user: User): boolean =>
user.orders.reduce((sum, order) => sum + order.amount, 0) > 100;
const getUserId = (user: User): string => user.id;
const richUserIds = users
.filter(hasTotalOrderAmountOver100)
.map(getUserId);
函數式寫法直接表達「找出訂單總額超過 100 的用戶 id」這個意圖。資料流以鏈式處理,無需中間變數或副作用,邏輯聚焦,一目了然。這種寫法不僅讓人類工程師更容易理解,也讓 AI 更能精確推理資料流與運算重點。
在 AI 輔助開發的時代,優先採用聲明式、純函數的資料處理方式,不只提升可維護性,也讓自動生成、重構與測試更加容易,是現代軟體開發的核心實踐。
結論
不論你是剛踏入開發領域的新手,還是經驗豐富的工程師,只要想提升個人生產力、打造穩健可維護的團隊程式碼,TypeScript 結合函數式設計,絕對是 AI 時代最值得投資的技術路線。這套思維不僅讓我們寫出更安全、好懂、好維護的程式,也讓 LLM(大型語言模型)更容易理解我們的意圖,產出高品質、可協作的代碼。
我不會說這是「最快」的寫法,但每當我用這種型別明確、資料流清晰的方式請 AI 幫忙時,信任感和產出品質真的大幅提升。如果你也常常覺得 AI 不懂你、產生的程式碼難以維護,非常建議試著用這種思維重寫看看,說不定會成為你開發效率的轉捩點。
下次你遇到寫不動、AI 無法理解的程式,不妨試著換個思維,讓每個小函數都更純粹、更具語意。你會發現,AI 不只更懂你,連你自己也更懂自己在寫什麼。當我們把邏輯寫給人看的同時,也要寫給 AI 看——而 TypeScript + 函數式設計正是這座橋樑。
你也想了解如何利用 AI 工具幫助開發與創作嗎?我會在 6 月 1 日於台北舉辦一場小型實體分享會。有興趣的話,你可以在這裡報名~
📍 台北中山區,捷運站步行 5 分鐘內,詳細地點將於報名後以 Email 通知。
📬 立即報名報名後我會寄出活動提醒信,也會附上簡報下載連結,期待現場見 👋