實作練習:TypeScript 基本型別
2025年7月26日
在上一篇文章中,我們探討了 TypeScript 的基礎構成要素:原始型別(string
、number
、boolean
)、陣列和物件等集合型別,以及型別推斷這個實用功能。
現在是時候將這些知識應用到實際練習中了。這些練習將幫助大家熟悉如何加上型別註解、捕捉常見錯誤,並理解 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 }[] = [
// ...
];
讓我們分析這個註解:
{ title: string; views: number; isPublished: boolean }
:這部分定義了單一 post 物件的藍圖。[]
:結尾的方括號告訴 TypeScript 我們處理的不是單一物件,而是這些物件的_陣列_。
現在,一旦我們套用這個型別,TypeScript 會立即在我們的資料中顯示三個錯誤:
{ title: "React State Management", views: "2.5k", isPublished: false }
- 錯誤:
Type 'string' is not assignable to type 'number'
。TypeScript 發現"2.5k"
是字串,不是數字。
- 錯誤:
{ 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 });
你的任務:
- 為
user
參數定義型別。bio
屬性應該是選擇性的。 - 為函式的回傳值加上型別註解。
- 修改函式以安全地處理
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
可能是string
或undefined
。): 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>