TypeScript 基本型別:理解原始型別與內建型別

2025年7月25日

💎 加入 E+ 成長計畫 與超過 700+ 位工程師一同在社群成長,並獲得更多深度的軟體前後端學習資源

在上一篇文章我們帶著大家安裝好 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。當你試圖在期待數字的地方傳入字串時,馬上就會得到清楚的錯誤訊息。

我們剛剛碰到的三個型別(numberstring,還有回傳型別 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 只能是 truefalse

處理集合資料

單一數值很有用,但實際的應用程式大多要處理資料集合。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,而不是為了每個變數都跳一堆圈圈。

總結

這些基本型別(stringnumberboolean、陣列和物件)構成了 TypeScript 型別系統的核心。從此不再會遇到神秘的執行期錯誤,而是在編輯器中就能得到清楚的警告。

TypeScript 不只是在 JavaScript 上加語法而已,它改變了你處理資料的方式。不用再希望 calculateTotalPrice 會收到數字,而是確實知道它會收到數字。不用再猜測那個文章物件是否有 title 屬性,TypeScript 會清楚告訴你裡面有什麼。

現在你已經知道 TypeScript 的基本構成元件了。但如果需要一個可能是字串或數字的變數怎麼辦?可選的物件屬性呢?或是可以容納多種型別的陣列?我們會在接下來的文章中介紹這些更複雜的情況。

現在,試著把一個簡單的 JavaScript 函式改成使用這些基本型別。你可能會驚訝地發現,即使是你認為運作完美的程式碼,TypeScript 也能抓到不少潛在的 bug。


加入 E+ 成長計畫

如果你覺得這篇內容有幫助,喜歡我們的內容,歡迎加入 ExplainThis 籌辦的 E+ 成長計畫,透過每週的深度主題文,以及技術討論社群,讓讀者在前端、後端、軟體工程的領域上持續成長。

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們