TypeScript - 任意類型:類型的瑞士軍刀

你好,未來的編程超新星們!今天,我們將深入TypeScript中最多用途(有時也會引起爭議)的特性之一:any類型。請繫好安全帶,因為我們即將踏上一段旅程,讓你說出:“任何事物皆有可能!”(對不起,我忍不住開了一個類型的玩笑!)

TypeScript - Any

什麼是任意類型?

在我們開始之前,讓我們想像你參加了一個百家宴。你帶來了一道菜,但你不確定其他人會帶來什麼。TypeScript中的any類型就像這樣——它可以持有任何類型的值,就像你在百家宴上的盤子可以盛放任何類型的食物一樣!

可以代表任何值

any類型正如其名——它在TypeScript中可以代表任何值。它就像一個萬用牌,說:“嘿,TypeScript,我可以成為我想成為的任何東西!”

讓我們看一些例子:

let myVariable: any = 42;
console.log(myVariable); // 輸出:42

myVariable = "Hello, World!";
console.log(myVariable); // 輸出:Hello, World!

myVariable = true;
console.log(myVariable); // 輸出:true

myVariable = [1, 2, 3];
console.log(myVariable); // 輸出:[1, 2, 3]

在這個例子中,我們聲明了myVariableany類型。然後我們給它賦予了不同類型的值——數字、字符串、布爾值和數組。TypeScript不會抱怨,因為any可以是,嗯,任何東西!

函數參數的任意類型

現在,假設你正在創建一個需要非常靈活的函數。你希望它能接受任何類型的參數。這就是any派上用場的地方!

function printAnything(arg: any): void {
console.log(arg);
}

printAnything(42);          // 輸出:42
printAnything("TypeScript"); // 輸出:TypeScript
printAnything([1, 2, 3]);    // 輸出:[1, 2, 3]

在這個例子中,我們的printAnything函數可以接受任何類型的參數。它就像一個友好的夜店保安,讓每個人都進來!

任意類型的對象

有時候,你可能想要創建一個可以有任何類型屬性的對象。讓我們創造一個神奇的袋子,它可以容納任何東西:

let magicalBag: { [key: string]: any } = {};

magicalBag.book = "Harry Potter";
magicalBag.wand = { wood: "Holly", core: "Phoenix feather" };
magicalBag.spells = ["Expelliarmus", "Lumos", "Accio"];

console.log(magicalBag);
// 輸出:
// {
//   book: "Harry Potter",
//   wand: { wood: "Holly", core: "Phoenix feather" },
//   spells: ["Expelliarmus", "Lumos", "Accio"]
// }

在這裡,magicalBag是一個可以有任何數量的屬性,每個屬性可以是任何類型的對象。它就像瑪麗·波平斯的手提包——它可以容納任何東西!

為什麼使用任意類型?

你可能會想,“如果TypeScript全是關於類型的,那我們為什麼要用any?”這個問題問得好!以下是一些any可能會有用的場景:

  1. 當處理動態內容(如來自API的數據)時
  2. 當逐漸將JavaScript項目遷移到TypeScript時
  3. 當使用沒有類型定義的第三方庫時

讓我們看一個處理動態內容的例子:

async function fetchUserData(userId: number): Promise<any> {
const response = await fetch(`https://api.example.com/users/${userId}`);
const userData = await response.json();
return userData; // 我們不知道userData的確切結構,所以使用'any'
}

// 使用
fetchUserData(123).then(user => {
console.log(user.name);  // TypeScript不會抱怨,即使'name'不存在
});

在這種情況下,我們不確定我們接收到的數據的結構,所以我們使用any來告訴TypeScript,“相信我,我知道我在幹什麼!”

類型斷言

有時候,你可能對一個值的類型比TypeScript更了解。這就是類型斷言的用處。它就像告訴TypeScript,“我知道你認為這是any,但請相信我,它實際上是特定類型。”

這樣使用類型斷言:

let someValue: any = "Hello, TypeScript!";
let strLength: number = (someValue as string).length;

console.log(strLength); // 輸出:20

在這個例子中,我們告訴TypeScript,“嘿,我知道someValueany類型,但我是確定它實際上是字符串。所以讓我把它當作字符串使用。”

警告:能力越大,責任越大

雖然any很強大,但它應該節制使用。記住,TypeScript的主要優勢是它的類型檢查。使用any,你基本上是在告訴TypeScript為那個變量關閉類型檢查。

以下是一個any可能導致運行時錯誤的例子:

let num: any = "42";
console.log(num.toFixed(2)); // 這將導致運行時錯誤!

TypeScript不會對這段代碼提出異議,但當你運行它時,會拋出錯誤,因為字符串沒有toFixed方法。

任意與未知:更安全的選擇

TypeScript 3.0引入了unknown類型,這是any的類型安全對應物。當any允許你無任何檢查地做任何事情時,unknown強制進行類型檢查。

讓我們比較一下anyunknown

let anyVar: any = 10;
let unknownVar: unknown = 10;

let s1: string = anyVar;    // 可以
let s2: string = unknownVar; // 錯誤:類型'unknown'不可賦值給類型'string'

// 我們在使用unknownVar之前需要檢查類型
if (typeof unknownVar === 'string') {
let s3: string = unknownVar; // 可以
}

正如你所見,unknown更安全,因為它強制你檢查類型之後才能使用。

方法表

以下是你可以與any一起使用的一些常用方法:

方法 描述 示例
typeof 返回一個字符串,指示未求值的運算元的類型 typeof anyVar === 'string'
instanceof 檢測構造函數的.prototype屬性是否在對象的原型鍊中 anyVar instanceof Array
類型斷言 告訴編譯器將值當作特定類型對待 (anyVar as string).length
類型守衛 用戶定義的類型預測,幫助縮小變量的類型範圍 if (isString(anyVar)) { ... }

記住,使用any時,你可以使用JavaScript中的任何方法,但你会失去TypeScript類型檢查的優勢。

至此,各位!你已經深入TypeScript中的any類型世界。記住,雖然any可以是一個強大的工具,但它就像超級英雄的力量——明智而謹慎地使用它。快樂編程,願類型總是站在你一邊!

Credits: Image by storyset