Table of contents
學習資源
JavaScript 的資料型別(Data types)一共有六種基本(Primitive)資料型別與物件(Object)。
基本資料型別包括:boolean
(布林值)、number
(數字)、string
(字串)、null
(無值)、undefined
(未定義值)與 symbol
(標識符,ES6)。
以下各型別的範例程式碼將會根據我的個人命名喜好進行調整,因此與書中的內容會有所不同。請依照喜歡的範例進行學習即可。
原始資料型別
布林值
在 TypeScript 中,用 boolean
來表示布林值:
let isActive: boolean = true;
function isEven(n: number): boolean {
return n % 2 === 0;
}
要注意的是,使用建構函式 Boolean
建立的是包裝了布林值的物件而不是布林值。其他除了 null
與 undefined
的基本型別(如 number
和 string
)也都有對應的建構函式(Number
和 String
),它們的功能類似於 Boolean
建構函式,後面不會重複說明此部份。
// 🔴
const isNewBoolean: boolean = new Boolean(false);
// error TS2322: Type 'Boolean' is not assignable to type 'boolean'.
// 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.
// 🟢
const boolObj: Boolean = new Boolean(false);
數字
在 TypeScript 中,用 number
來表示數字:
(註:書中原本的變數命名部分使用縮寫,我不太理解原意,因此請 ChatGPT 將變數重新命名並加上註解,使其代表意義更清楚明確)
// 十進位
let decimalNumber: number = 6;
// 十六進位
let hexadecimalNumber: number = 0xf00d;
// 二進位 (ES6 中的二進位制表示法)
let binaryNumber: number = 0b1010;
// 八進位 (ES6 中的八進位制表示法)
let octalNumber: number = 0o744;
// NaN (Not a Number) 值,表示非數字結果
let notANumberValue: number = NaN;
// 無窮大數字值
let infinityValue: number = Infinity;
編譯成 JavaScript 的結果:
var decimalNumber = 6;
var hexadecimalNumber = 0xf00d;
var binaryNumber = 10; // 注意,編譯後的二進位數字將被轉換為十進位數字
var octalNumber = 484; // 注意,編譯後的八進位數字將被轉換為十進位數字
var notANumberValue = NaN;
var infinityValue = Infinity;
字串
在 TypeScript 中,用 string
來表示字串:
let name: string = "John";
function greet(person: string): string {
return `Hello, ${person}!`;
}
編譯成 JavaScript 的結果:
var name = "John";
function greet(person) {
return "Hello, " + person + "!";
}
無值與未定義值
在 TypeScript 中,null
和 undefined
是特殊的子型別,可以賦值給其他型別(例如,string
或 number
)的變數(除非啟用 strictNullChecks
選項):
let undefinedValue: undefined = undefined;
let nullValue: null = null;
// 這些都不會報錯
let x: number = null;
let y: string = undefined;
在啟用 strictNullChecks
選項時,需要明確地指定變數可以為 null
或 undefined
:
let x: number | null = null;
let y: string | undefined = undefined;
「子型別」什麼是意思?
ChatGPT:「子型別(Subtype)是一個術語,用於描述型別系統中的一種關係。當一個型別 A 是另一個型別 B 的子型別時,意味著 A 的實例可以替代 B 的實例,而不會導致程式出錯或不正確的行為。在物件導向編程中,子型別通常與繼承和多態性有關。」
嗯~有看沒有懂,只好變身十歲小孩再問一次。它第一次是用貓狗都是動物來解釋,但我還是聽不懂(就不貼上來了),第二次它改用不同顏色的球來比喻。
ChatGPT:『想像一下,你有一個大盒子,裡面裝滿了各種顏色的小球。這個大盒子就代表「所有顏色的球」類別。現在,我們可以在裡面找到紅色的球、藍色的球和綠色的球等等。這些特定顏色的球都是「所有顏色的球」這個大類別的一部分,因為它們都是球。
所以,在這個例子中,「紅色的球」、「藍色的球」和「綠色的球」等等,都是「所有顏色的球」的子型別。這是因為它們都具有「球」的基本特徵,但同時也具有自己獨特的顏色。
子型別的概念就是讓我們可以在需要的時候,把特定顏色的球當作「所有顏色的球」來看待和使用。因為無論顏色如何,它們都是球,所以可以互相替換使用。』
到這裡我有點誤解子型別的意思,會發現是因為我把顏色替換成五個基本型別標籤,問 ChatGPT 這樣的理解正不正確。我以為
null
跟undefined
是其他基本型別的子型別,經過 ChatGPT 的解釋(見下方),才恍然大悟為什麼會說null
跟undefined
是「特殊的」子型別。
「子型別通常在物件導向編程中,涉及到繼承和多態性。然而,boolean
、string
、number
、null
和 undefined
這些基本型別是 TypeScript 中最基本的數據類型,它們之間沒有繼承或子型別的關係。」
「在 TypeScript 中,null
和 undefined
有時可以被賦予其他基本型別的變量,這是因為在預設情況下,TypeScript 允許 null
和 undefined
賦值給其他所有型別。然而,這並不意味著 null
和 undefined
是其他基本型別的子型別。」
如何啟用 strictNullChecks
選項?
在 tsconfig.json
將 strictNullChecks
選項設定為 true
:
{
"compilerOptions": {
"strictNullChecks": true,
// 其他編譯選項...
}
}
特殊型別
無返回值
(註:這裡沒有跟書中一樣用「空值」來說明,是為了避免自己把 void
跟 null
搞錯)
在 TypeScript 中,void
是一個特殊型別,用來表示一個函式沒有返回值:
function printMessage(message: string): void {
console.log(message);
}
用 void
型別作為變數的型別沒有太大的用處,因為該變數只能賦值為 null
和 undefined
這兩個特殊的子型別:
let voidVariable: void = undefined;
「所以可以理解 void
跟其他基本型別是平行的囉?」我再次向 ChatGPT 確認自己的理解。
ChatGPT:「是的,您可以將 void
視為與其他基本型別(如 boolean
、string
、number
、null
和 undefined
)平行的一個型別。在 TypeScript 中,void
是一個獨立的基本型別,主要用於表示一個函式沒有返回值。」
任意值
在 TypeScript 中,any
是一個特殊型別,用來表示允許將變數標記為可以接受任何型別的值:
let anything: any = "Hello";
anything = 42;
anything = [1, 2, 3];
anything.foo = "bar";
anything();
(OS:看來 any
是 TypeScript 界的任意門)
對比基本型別,當賦值型別與定義的不相同,型別系統是會逼逼叫的(還記得昨天的解釋嗎?想不起來可以回到昨天的筆記複習喔!):
let myLuckyNumber: number = 7;
myLuckyNumber = "7";
// error TS2322: Type 'string' is not assignable to type 'number'.
另外,如果變數被標記為任意值 any
的型別,就算存取不存在的屬性或呼叫不存在的方法,通通給過不會報錯,根本開大絕!
let someObject: any = { name: "John" };
console.log(someObject.age); // 不會報錯,即使屬性 'age' 不存在
let someValue: any = "Hello";
someValue.unknownMethod(); // 不會報錯,即使方法 'unknownMethod' 不存在
當我們對 any
型別的變數進行操作時,返回的值也會被視為 any
型別。表示可以將返回值再賦值給任何其他型別的變數,或者對返回值進行任何操作,也都不會報錯。
請注意,關於「未宣告型別的變數」這段描述「變數如果在宣告的時候,未指定其型別,那麼它會被識別為任意值型別」並不十分精確。
更準確地說是「變數如果在宣告的時候,未指定其型別且沒有初始值時,那麼它會被識別為任意值型別」,詳細原因請見下方說明。
還記得昨天有提到「 TypeScript 會透過『型別推論』的規則來推斷其型別」,那它靠什麼來推斷呢?「初始值」。反之,若是變數沒有給初始值時,TypeScript 就只能推空氣,像空氣般的型別我想就是任意值 any
了。
補充:本來是用「初始化的值」與「若是變數沒有初始化」,但之後想起用 var
宣告變數其實是會自動初始化為 undefined
的,因此將用詞改為「初始值」。
接下來我會放上 ChatGPT 給的範例來做說明。
以下是一個未指定型別的變數宣告範例:
let someVariable = "Hello, World!";
在此範例中,因為變數 someVariable
初始值為一個字串,TypeScript 編譯器會推斷其型別為 string
。如果初始值是數字、布林值等,編譯器會相應地推斷出其他型別。
然而,如果變數在宣告時沒有給初始值,TypeScript 編譯器無法推斷出變數的型別,因此它將使用 any
型別:
let someVariable; // 等同於 let someVariable: any;
在這種情況下,變數 someVariable
的型別將被設為 any
,這意味著可以將任何型別的值賦給它,而編譯器不會報告任何錯誤。
讀到這我不禁心想:用任意值難道不就跟寫 JavaScript 一樣,門戶大開,那我這麼辛苦學 TypeScript 幹嘛?
不過當我回想起之前重構時,看著那一大堆的紅色錯誤提示,真的有股衝動想直接 any
催下去,假裝什麼都沒看見,都是我眼睛業障重。
瞬間了然於心,任意值還是有存在的必要性。(合掌)