實作練習:TypeScript 基本型別

2025年7月26日

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

在上一篇文章中,我們探討了 TypeScript 的基礎構成要素:原始型別(stringnumberboolean)、陣列和物件等集合型別,以及型別推斷這個實用功能。

現在是時候將這些知識應用到實際練習中了。這些練習將幫助大家熟悉如何加上型別註解、捕捉常見錯誤,並理解 TypeScript 如何引導我們寫出更可預測的程式碼。


題目 1:修正原始型別(簡單)

以下程式碼片段充滿了型別錯誤。開發者混淆了字串、數字和布林值的使用。

let postTitle: string = 123;
let commentCount: number = "50 comments";
let isDraft: boolean = "true";

你的任務:修正變數賦值,使其符合宣告的 TypeScript 型別。接著,在程式碼的第二部分中加上正確的型別註解,運用你所學到的知識。

// 第二部分:加上型別註解
// 在這個部分,你會看到沒有任何型別的變數
// 你的任務是根據賦值內容加上正確的型別註解
let authorName = "Alice";
let postViews = 2450;
let isFeatured = false;

<details> <summary>查看說明</summary>

讓我們來分析這個問題。第一部分的問題在於型別不匹配。TypeScript 告訴我們,賦予的值無法放入我們定義的「容器」中。

以下是修正後的程式碼:

// 值必須是字串
let postTitle: string = "My First Post";

// 值必須是數字
let commentCount: number = 50;

// 值必須是布林值(true 或 false)
let isDraft: boolean = true;

這裡的關鍵概念是,型別註解就像是一個契約。當你寫 let postTitle: string 時,你承諾這個變數將_始終_持有字串。TypeScript 的職責是確保這個承諾得到履行。

現在談第二部分,我們需要自己加上型別。

let authorName: string = "Alice";
let postViews: number = 2450;
let isFeatured: boolean = false;

你可能會想:「但教學文章不是說 TypeScript 可以推斷這些型別嗎?」沒錯,絕對可以!如果你寫 let authorName = "Alice";,TypeScript 會正確推斷型別為 string。然而,這個練習的目的是讓你熟悉_明確_寫出型別的語法。理解明確的寫法有助於你理解 TypeScript 在隱式情況下為你做了什麼。

</details>


題目 2:為物件陣列加上型別(簡單-中等)

你從 API 取得了一個部落格文章清單。下方的程式碼試圖處理這些資料,但因為沒有定義型別而產生錯誤。拼字錯誤(titel 而非 title)沒有被捕捉到,而且程式碼試圖在數字上使用字串方法。

const posts = [
  { title: "TypeScript Basics", views: 1500, isPublished: true },
  { title: "React State Management", views: "2.5k", isPublished: false },
  { titel: "Advanced CSS", views: 800, isPublished: true },
];

posts.forEach((post) => {
  // 這會因為拼字錯誤 'titel' 而產生執行時期錯誤
  console.log(`Post: ${post.title.toUpperCase()}`);
  // 這也會在第二個文章產生執行時期錯誤
  console.log(`Views: ${post.views.toFixed(2)}`);
});

你的任務:為 posts 陣列建立型別註解。它應該是一個物件陣列,每個物件都有 title(字串)、views(數字)和 isPublished(布林值)。修正陣列中的資料以符合你定義的型別。

<details> <summary>查看說明</summary>

這個問題的核心是一個非常常見的情境:處理來自外部來源(如 API)的資料。資料可能不完全一致,這正是 TypeScript 發揮作用的地方。

首先,讓我們定義單一 post 物件的「形狀」。我們需要一個具有三個特定屬性及其對應型別的物件。

const posts: { title: string; views: number; isPublished: boolean }[] = [
  // ...
];

讓我們分析這個註解:

  1. { title: string; views: number; isPublished: boolean }:這部分定義了單一 post 物件的藍圖。
  2. []:結尾的方括號告訴 TypeScript 我們處理的不是單一物件,而是這些物件的_陣列_。

現在,一旦我們套用這個型別,TypeScript 會立即在我們的資料中顯示三個錯誤:

  1. { title: "React State Management", views: "2.5k", isPublished: false }
    • 錯誤Type 'string' is not assignable to type 'number'。TypeScript 發現 "2.5k" 是字串,不是數字。
  2. { titel: "Advanced CSS", views: 800, isPublished: true }
    • 錯誤Property 'title' is missing...Object literal may only specify known properties, and 'titel' does not exist...。TypeScript 捕捉到了我們的拼字錯誤!

這很棒。我們甚至還沒執行程式碼,就已經找出了所有的錯誤。

透過預先定義資料的預期結構,我們讓程式碼更加穩健,並捕捉到了原本會讓應用程式在執行時期崩潰的錯誤。

</details>


題目 3:為具有選擇性資料的函式加上型別(中等)

你正在撰寫一個建立使用者個人檔案摘要的函式。該函式應該接受一個使用者物件,但有時候 bio 屬性可能會遺失。目前的 JavaScript 程式碼沒有處理這種情況,可能導致崩潰。

function createUserSummary(user) {
  const summary = `User: ${user.name}, Age: ${user.age}.`;
  // 如果 bio 遺失,這行會崩潰
  const bioSummary = `Bio: ${user.bio.substring(0, 10)}...`;
  return summary + " " + bioSummary;
}

// 正常運作
createUserSummary({
  name: "Bob",
  age: 35,
  bio: "A software developer from New York.",
});

// 會崩潰
createUserSummary({ name: "Charlie", age: 42 });

你的任務

  1. user 參數定義型別。bio 屬性應該是選擇性的。
  2. 為函式的回傳值加上型別註解。
  3. 修改函式以安全地處理 bio 未提供的情況。

<details> <summary>查看說明</summary>

這個問題結合了幾個概念:函式參數型別、具有選擇性屬性的物件型別,以及處理可能遺失的值(undefined)。這是一個非常真實的情境。

讓我們從定義 user 物件的型別開始。我們知道 name 是字串,age 是數字。bio 屬性是字串,但它可能不存在。我們可以在冒號前加上 ? 來標記屬性為選擇性。

function createUserSummary(user: {
  name: string;
  age: number;
  bio?: string;
}): string {
  // ... 函式主體
}

讓我們看看這些註解:

  • bio?: string? 使 bio 屬性變為選擇性。TypeScript 現在理解 user.bio 可能是 stringundefined
  • ): string:我們明確聲明這個函式將始終回傳字串。TypeScript 可以推斷這一點,但明確聲明讓函式的契約更清楚。

現在,如果我們試圖直接使用 user.bio,TypeScript 會阻止我們:

// const bioSummary = `Bio: ${user.bio.substring(0, 10)}...`;
// 錯誤:'user.bio' is possibly 'undefined'.

這是 TypeScript 的安全網在運作。它強制我們考慮 undefined 的情況。修正方法是在嘗試使用 bio 之前檢查它是否存在。

以下是完整且安全的解決方案:

function createUserSummary(user: {
  name: string;
  age: number;
  bio?: string;
}): string {
  let summary = `User: ${user.name}, Age: ${user.age}.`;

  // 檢查選擇性屬性是否存在
  if (user.bio) {
    // 在這個區塊內,TypeScript 知道 user.bio 是字串
    const bioSummary = ` Bio: ${user.bio.substring(0, 10)}...`;
    summary += bioSummary;
  }

  return summary;
}

// 正常運作
console.log(
  createUserSummary({
    name: "Bob",
    age: 35,
    bio: "A software developer from New York.",
  })
);

// 現在也能正常運作,不會崩潰!
console.log(createUserSummary({ name: "Charlie", age: 42 }));

透過使用 ?bio 屬性變為選擇性,我們告訴 TypeScript 資料中存在不確定性。作為回報,TypeScript 強制我們撰寫更安全的程式碼來處理兩種情況,防止了常見的執行時期錯誤。

</details>

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