接續昨天的文章,我們刻意斷言斷錯型別,TypeScript説「你斷言斷錯型別了,如果你是故意的,要解決這個error的話,要先斷言成unknown
唷」。我們今天來講講unknown
到底是什麼?還有一些比較特殊的型別。
顧名思義就是未知(廢話),它的性質跟any
有些類似,可以代表任何值,但又有所不同,我們來看例子:
function sayHi1(msg: any) {
console.log(msg.toUpperCase())
//正常運作不噴錯
}
function sayHi2(msg: unknown) {
console.log(msg.toUpperCase())
//Object is of type 'unknown'.
}
簡單來說,對一個變數/參數斷言他是any
,我們可以對他取用任何方法/屬性,都不會報錯、可以正常編譯,哪怕它在runtime時真的是錯的。但unknown
就不同了,因為對TypeScript來講,他是未知的,所以對他呼叫任何"可能"的方法,都會提前報錯,免得你在程式碼上線後,發生未知的錯誤。
unknown
跟any
到底要怎麼選擇,簡單來說,any
比較隨性,TypeScript不會對任何設為any
的值進行檢查,所以,就跟寫原生JS一樣,失去了使用TypeScript的優點。有些時候,我們可能會先宣告一個,還不確定型別的變數,這時就很適合給他unknown
而非any
,如此一來,我們在還沒進一步對這個變數進行型別斷言前,對該變數進行任何的操作都會報錯,可以讓我們很快的在編譯前發現錯誤。
而剛剛有提到any
相對比較隨性,我們來看下面例子:
function casual(s){
console.log(s.toFixed(2))
}
在這個情況下,我們的參數s
,在沒有設定任何型別時,會被TypeScript推測為是any
,因此對他進行任何方法都不會報錯,但我們引入TypeScript就是希望讓我們的code更加安全、更好維護,因此這行為不是我們樂見的。在未來會提到的TypeScript設定檔(config)中,其中有一項flag叫做noImplicitAny
,開啟後,當我們遇到上述這種沒設定型別的情況,就會直接噴錯,避免我們"忘了"加上型別。
never
滿有趣的,就連官方對他的說明都很有限,通常使用於對函式回傳值型別宣告
function fail(msg: string): never {
throw new Error(msg);
}
通常是用於1.函式拋出了例外 2.程式在執行過程中被中止了。
拋出例外還算好理解,但第二點...來看一下例子:
function fn(x: string | number) {
if (typeof x === "string") {
// do something
} else if (typeof x === "number") {
// do something else
} else {
x; // has type 'never'!
}
}
上面是官方文件提供的範例,雖然有點雞肋,但我們的確能知道最後一個else
block是絕對到不了的,所以那邊的x
他是一個完全不可能被回傳/執行的值,它很明顯的是never
("絕不"會被回傳/執行)。
既然剛剛有提到never
,那就順便提到類似的void
吧,這個的使用率絕對比never
來得多,void
代表的是「函式沒有回傳任何值」,注意,不是沒有return
喔,是沒有回傳任何值。
function hello(){
return;
}
//TypeScript也會覺得這是void,因為我們完全沒有回傳值
function hello(){
console.log("hello~~")
}
這兩個情境都是將型別設定於"回傳值"。那,我們可不可以為將變數設定成void
呢?呃,是...是可以啦,但沒什麼意義。
let nothing: void = undefined;
let number: void = 1;
//Type 'number' is not assignable to type 'void'.
只有null
跟undefined
可以指派void
型別,但我還沒想到合宜的使用情境lol。
以上是我在初步接觸TypeScript時,所看到比較特別的型別,畢竟就算看得懂它字面上的意義,我們還是得進到官方文件,去了解它到底該怎麼使用、要解決原生JavaScript的什麼問題。在這邊簡單介紹給大家,希望大家能分享一下你們實際使用到這些型別的情境為何。