TypeScript 是 JavaScript 的超集合,最終還是編譯成 JavaScript,除了原本 JavaScript 的型別系統,TypeScript 還新增了一些特殊型別,例如:void、any、never 與 unknown。
此篇先以 void 與 any 介紹為主:
void
void 表示沒有 return 返回值的函數回傳值(空值)型別。
在撰寫 JavaScript 函式、方法時,函式不一定要 return 返回值出來,它不一定是有輸入就有輸出的 pure function。例如 JavaScript 我們可以這樣寫:
let a = 1;
function add(){
a = a + 1
console.log(a)
}
add()
先不討論 add 這個函式乾不乾淨,至少上面程式碼是符合 JavaScript 規範的。而在 TypeScript 的設計裡,它預期每個函式、方法會有回傳 return 的機制在,但是我們在 add 函式內直接修改外部變數,函式並沒有 return 任何值到外部。
當函式沒有任何程式段落 return 值到外部時,就(會)要用 void 來規範,表示這個函式的回傳值空。所以上面的函式,若不使用型別推斷而是自己加上註記,在 TypeScript 就可以寫成這樣:
function add(): void {
a = a + 1;
console.log(a);
}
另外,使用 void 限制型別再賦值 null 或 undefined 沒有意義,雖然 null 和 undefined 是所有型別的子型別,除非改 config 否則這麼寫不會報錯
let nothing: void = null;
但這樣寫完全沒意義,不如直接限制 null 或 undefined,void 還是用在函數的回傳型別為主。
其他的強型別程式語言也有 void 的設計,只是 JavaScript 沒有,為了避免和其他前端工程師討論中文空值時不知道是指 void 還是 null,建議用英文原文術語 void 討論會好一點。
any
any 又稱為 TypeScript 的通用型別或任意型別,是所有型別的聯集,使用 any 會允許 TypeScript 在之後仍可以賦值為任意型別。
let a: any = 100
a = "100"
a = []
不過這樣寫型別規範,使用 TypeScript 就沒意義了。
此外 any 比較常出現在型別推論之時。當我們進行延遲性指派(先宣告變數再賦值),TypeScript 會替預先宣告好的變數定義為 any 型別。
let a;
a = 100;
要注意和原本 JavaScript 的差異:延遲性指派預先宣告變數時,JavaScript 新變數預設型別為 undefined,而 TypeScript 除非到 tsconfig.json 中設定 noImplicitAny,否則預設新變數型別為 any。
還有要注意的是, any 不只允許被賦值為任意型別,在 any 值上訪問任何屬性或呼叫方法都是允許的,例如這樣註記 : number
會提示:
let year: number = 2021;
console.log(year.a)
如果在 VS Code 這樣寫 TypeScript 就會提示錯誤
仍然編譯的話就會報錯,這很合理,year 變數沒有 a 屬性,數值原形練也沒有 a 存在,編譯時就報錯合理。然而,若我們將 : number
改成 : any
,TypeScript 會允許編譯成功。
let year: any = 2021;
console.log(year.a)
之後執行 JavaScript 時,因為沒有 a 的存在,所以 JavaScript log 就會印出 undefined,但是這樣寫沒有意義..。
撇開型別推論,開發者主動註記 any 更像是 TypeScript 給予開發者的一種彈性,主動註記或斷言 any 不是不能用(例如接手前人 code 改成 TypeScript 時),但用前先想想,因為強迫開發者在撰寫時先限制型別正是 TypeScript 的用意之一,若版本允許,使用 unknown 會比 any 更好一點,若之後有時間會再分享 unknown 與 never 的介紹。
參考
TypeScript 新手指南
TypeScript: Typed JavaScript at Any Scale.
kobo 電子書:讓TypeScript成爲你前端開發的ACE!
Typescript 初心者手札