TypeScript 基本型別:理解原始型別與內建型別
2025年7月25日
在上一篇文章我們帶著大家安裝好 TypeScript,在這篇文章我們會來談 TypeScript 最基礎的型別。在最開始,讓我們用一個具體的例子,來談當導入型別後,能夠帶來什麼好處。
假設今天在 ExplainThis 的 E+ 成長計畫中,我們想加上一個訂閱方案價格計算的功能。使用者選擇一個方案,再加上一些額外的內容訂閱,會算出最終的價格,這時可以有個簡單的計算函式;但這種看似簡單的函式,在 JavaScript 當中可能會有下面這種問題:
function calculateTotalPrice(planPrice, extra) {
return planPrice + extra;
}
const totalPrice = calculateTotalPrice("29", 15);
console.log(totalPrice); // "2915" - 怎麼會變成 2915?
在上面這個函式中,因為 JavaScript 沒有型別的檢查,所以傳入字串的 "29"
不會有任何報錯。然而,假如今天傳入的是字串,JavaScript 會自動做型別轉換,所以原本預期算出 44
,結果卻變成 2915
,這種狀況正是 TypeScript 的型別系統能幫我們預防的問題。
上面的價格計算例子外,還有很多在 JavaScript 中,因為自動型別轉換,而導致的意外,這些都可能讓程式實際上線後造成意想不到的 bug。舉例來說,以下這些運算在 JavaScript 中都是可以正常執行,但結果可能不是大家所預期的。
"5" + 3; // "53" (數字變成字串)
true + 1; // 2 (布林值變成數字)
null + 5; // 5 (null 變成 0)
如果改成用 TypeScript,而是要求你明確說明程式碼預期要處理什麼型別的資料。雖然一開始可能會覺得麻煩,但它能在程式碼執行之前就抓到這些混合型別的錯誤,這真的蠻實用的。
你的第一個 TypeScript 型別
讓我們用 TypeScript 最基本的型別來修正那個價格計算器。這些是你會經常使用的基本元件:
function calculateTotalPrice(planPrice: number, extra: number): number {
return planPrice + extra;
}
const totalPrice = calculateTotalPrice("29", 15); // Error: Argument of type 'string' is not assignable to parameter of type 'number'
TypeScript 剛剛救了你一命,避免了字串串連的 bug。當你試圖在期待數字的地方傳入字串時,馬上就會得到清楚的錯誤訊息。
我們剛剛碰到的三個型別(number
、string
,還有回傳型別 number
)都屬於 TypeScript 的原始型別。讓我們來看看所有的原始型別:
// 你最常用的三個原始型別
let articleTitle: string = "TypeScript 基礎";
let viewCount: number = 1250;
let isPublished: boolean = true;
// TypeScript 會防止型別不匹配
articleTitle = 42; // Error: Type 'number' is not assignable to type 'string'
viewCount = "一千"; // Error: Type 'string' is not assignable to type 'number'
isPublished = "是的"; // Error: Type 'string' is not assignable to type 'boolean'
這些型別的運作方式跟你在 JavaScript 中預期的完全一樣,只是多了安全防護。string
用來存放文字,number
用來存放任何數值(不管是整數還是小數),boolean
只能是 true
或 false
。
處理集合資料
單一數值很有用,但實際的應用程式大多要處理資料集合。TypeScript 擴展了原始型別的概念,讓陣列和物件也能以型別安全的方式運作。
TypeScript 中的陣列會指定它們包含什麼型別的元素:
const articleViews: number[] = [1250, 892, 2100, 456];
const categories: string[] = ["TypeScript", "JavaScript", "React"];
const publishedFlags: boolean[] = [true, false, true];
// TypeScript 會防止你意外混用型別
articleViews.push("熱門"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'
categories.push(100); // Error: Argument of type 'number' is not assignable to parameter of type 'string'
number[]
語法就是「數字陣列」的意思。TypeScript 會確保你加到陣列中的每個元素都是數字,string[]
和 boolean[]
也是同樣的概念。
物件的運作方式類似,但你要定義物件的結構:它應該有什麼屬性,以及這些屬性應該是什麼型別:
const article: { title: string; wordCount: number; isPublished: boolean } = {
title: "TypeScript 基本型別",
wordCount: 2500,
isPublished: true,
};
// TypeScript 會檢查你是否提供了所有必要的屬性
const incompleteArticle: { title: string; wordCount: number } = {
title: "React Hooks 指南",
// Error: Property 'wordCount' is missing
};
// 還會防止屬性名稱的拼字錯誤
console.log(article.titel); // Error: Property 'titel' does not exist. Did you mean 'title'?
這種物件型別語法看起來可能有點冗長,但它能抓住許多常見的 bug。屬性名稱拼錯、缺少屬性、值的型別錯誤等等,TypeScript 在程式碼執行前就能全部抓到,畢竟誰都不想犯這種基本錯誤。
當 TypeScript 自己推論型別
可能會讓你驚訝的是,其實你不一定要寫出這些型別註解。TypeScript 通常夠聰明,能根據你指派的值來推論出應該是什麼型別。
// TypeScript 會自動推論這些型別
let welcomeMessage = "歡迎來到 ExplainThis!"; // TypeScript 知道這是 string
let totalArticles = 42; // TypeScript 知道這是 number
let siteIsLive = false; // TypeScript 知道這是 boolean
// 你可以試著指派錯誤的型別來驗證
welcomeMessage = 123; // Error: Type 'number' is not assignable to type 'string'
totalArticles = "四十二"; // Error: Type 'string' is not assignable to type 'number'
這個功能叫做型別推論,是 TypeScript 最棒的特色之一。你可以得到型別安全,而且不用為每個變數都加上註解。
陣列和簡單物件也受惠於型別推論:
const articleIds = [1, 2, 3, 4]; // TypeScript 推論為 number[]
const mixed = ["TypeScript", 42, true]; // TypeScript 推論為 (string | number | boolean)[]
const author = { name: "Alice", articlesWritten: 30 }; // TypeScript 推論為 { name: string; articlesWritten: number }
但這裡有個重點要注意。TypeScript 只能在看得到初始值的時候推論型別。函式參數就是一個需要明確標註的完美例子:
// TypeScript 無法猜測你會傳什麼型別給函式,所以需要明確註解
function greetReader(name: string, articlesRead: number): string {
return `哈囉 ${name},你已經在 ExplainThis 上讀了 ${articlesRead} 篇文章!`;
}
// 但它可以根據回傳內容推論回傳型別,所以這裡不需要註解
function calculateTotalViews(todayViews: number, yesterdayViews: number) {
return todayViews + yesterdayViews; // TypeScript 推論這會回傳 number - 不需要寫 ': number'
}
可以這樣想:TypeScript 看得到你在函式內部做什麼,但沒辦法預測你會從外部傳什麼參數給它。
處理可能不存在的值
實際的應用程式經常要處理可能不存在的資料。使用者可能沒有提供電話號碼,或者 API 呼叫可能回傳 null 而不是正常資料。TypeScript 為這些情況提供了特定的型別:
let authorBio: string | null = null; // 可能是字串或 null - 告訴 TypeScript 這個變數可以是空的
let socialHandle: string | undefined = undefined; // 可能是字串或 undefined - 另一種表示值可能不存在的方式
// TypeScript 會強制你處理 null 的情況 - 沒有 null 檢查的話會出現錯誤
function sendNewsletter(email: string | null) {
// 這會造成錯誤:console.log(`寄送電子報到 ${email.toUpperCase()}`);
// 因為 email 可能是 null,而 null 沒有 toUpperCase() 方法
if (email !== null) {
console.log(`寄送 ExplainThis 電子報到 ${email.toUpperCase()}`);
} else {
console.log("沒有提供電子報的電子信箱");
}
}
|
符號建立了所謂的聯合型別,我們會在後面的文章中深入探討。現在只要知道 string | null
代表「這可能是字串,也可能是 null」就夠了。
基本型別速查表
既然你已經看過這些型別的實際應用,我們來建立一個日常會用到的基本 TypeScript 型別速查表:
// 原始型別
let articleContent: string = "學習 TypeScript 基礎...";
let readingTime: number = 5;
let isFeatured: boolean = true;
// 陣列型別
let viewCounts: number[] = [100, 250, 180];
let tags: string[] = ["typescript", "javascript"];
// 物件型別
let reader: { name: string; articlesRead: number } = {
name: "Alice",
articlesRead: 15,
};
// 函式型別
function calculateEngagement(views: number, likes: number): number {
return views + likes;
}
// 處理可能不存在的值
let authorTwitter: string | null = null;
let featuredImage: string | undefined = undefined;
這五個類別(原始型別、陣列、物件、函式,以及可為空的型別)涵蓋了你會寫的大部分 TypeScript 程式碼。熟悉這些之後,就能理解其他所有東西的基本構成元件。
何時要明確標註型別
相信讀者們這時會問:什麼時候應該加上型別註解,什麼時候可以依賴推論?這裡有個實用的經驗法則:
讓 TypeScript 自己推論的時機:
- 立即指派值的時候:
let title = "TypeScript 指南"
- 從上下文就能明顯看出型別:
const viewCounts = [100, 250, 180]
- 處理簡單運算式:
const totalViews = todayViews + yesterdayViews
需要明確標註的時機:
- 定義函式參數:
function publishArticle(title: string)
- 你想要強制特定的結構:
const article: { title: string; wordCount: number } = {}
- 推論出的型別對你的需求來說太窄或太寬
這種平衡讓你得到安全性,又不會讓程式碼被不必要的型別註解搞得亂七八糟。重點是讓 TypeScript 抓到真正的 bug,而不是為了每個變數都跳一堆圈圈。
總結
這些基本型別(string
、number
、boolean
、陣列和物件)構成了 TypeScript 型別系統的核心。從此不再會遇到神秘的執行期錯誤,而是在編輯器中就能得到清楚的警告。
TypeScript 不只是在 JavaScript 上加語法而已,它改變了你處理資料的方式。不用再希望 calculateTotalPrice
會收到數字,而是確實知道它會收到數字。不用再猜測那個文章物件是否有 title
屬性,TypeScript 會清楚告訴你裡面有什麼。
現在你已經知道 TypeScript 的基本構成元件了。但如果需要一個可能是字串或數字的變數怎麼辦?可選的物件屬性呢?或是可以容納多種型別的陣列?我們會在接下來的文章中介紹這些更複雜的情況。
現在,試著把一個簡單的 JavaScript 函式改成使用這些基本型別。你可能會驚訝地發現,即使是你認為運作完美的程式碼,TypeScript 也能抓到不少潛在的 bug。
加入 E+ 成長計畫
如果你覺得這篇內容有幫助,喜歡我們的內容,歡迎加入 ExplainThis 籌辦的 E+ 成長計畫,透過每週的深度主題文,以及技術討論社群,讓讀者在前端、後端、軟體工程的領域上持續成長。