哇,居然已經到第十天了,不知不覺也完成了1/3,有些感動,希望之後能穩定發揮!
目前我們都只聊到很粗淺的TypeScript概念,但沒有這些概念,也沒辦法繼續往下走,今天就讓我們複習一下到目前為止的內容吧。
TypeScript的出現,就是為了要解決JavaScript作為「弱型別的語言」而生的,弱型別大概可以理解為,我們在宣告一個變數時,不用事先為他定義一個型別,在宣告後,也可以將變數從ex:字串->轉為數字。這點不全然是壞事,但對於要從強型別語言跨來JavaScript的開發者來講,就會非常不習慣,且程式也有可能因為這個特性,發生非預期的錯誤,因此才需要有TypeScript來幫我們減少可能發生的錯誤。對,他的確有學習及使用成本,但整理來說是值得的。
TypeScript有很多種型別可以讓我們使用,最能直接想到的就是string
、number
、boolean
。我們可以在程式碼當中先將特定變數設定成這些型別,舉例來說,若我們的函式中有個參數是age: number
,我們在函式內部若想對age
使用字串才有的方法ex: age.toUpperCase()
,程式碼便會在編譯前就直接秀出錯誤,避免我們到很後期才發現這個錯誤行為。不僅如此,在你打字的過程中,一打完age.
的那個.
,編輯器就會直接秀出只有數字型別才會有的方法推薦了。如果你的參數是個物件,在函式內也一樣會產生相對應的屬性名稱提示。
我們也學到了型別聯集Type Union,讓我們可以不侷限於一個特定型別。若我們將函式參數設定為age: number | string
,在呼叫時便能帶入數字或字串都不會報錯,但必須注意的是,函式內部若想直接操作age
而沒有先進行型別防衛Type Guard的話,就必須使用"數字跟字串"都有的方法。要型別防衛,最簡單的方法就是多寫一個判斷式:if (typeof age === "number"){就做number的事}
,便能化解這個狀況。
如果覺得型別防衛很麻煩,那我們可以進行型別斷言Type Assertion,舉例來說,我們的函式如果會視情況回傳數字或字串,我們可以在回傳時"斷言"他會是什麼型別,let result = foo() as number
,這樣就能讓編輯器知道”你知道自己在做什麼“,但斷言不代表他會強制幫你將函式內的執行結果"轉型",你只能斷言他原本就會回傳的特定型別,像這例子我們就只能斷言他是number
或是string
,但如果你真的想要將他斷言成其他型別,就先將它斷言成any/unknown
,再斷言成你想要的型別就可以了。所以斷言要謹慎,就算編輯器懂了,不代表其他人類看得懂&能理解。
剛剛有提到unknown
跟any
,這兩個也是TypeScript的型別,不一樣的是,TypeScript比較不會去管你的any
,你要對是any
的變數做任何事都可以,但如果是unknown
,因為是未知,所以TypeScript會叫你先確認了他的型別,再進行後續操作才會比較安全。還有兩個比較少使用到的never
跟void
,never
是代表不可能被回傳/執行的值、void
則代表沒有回傳任何值(像是單純的console.log
)。
如果一直要在參數中寫上number | string
,有點麻煩。我們可以使用型別別名Type Alise來將特定組合的型別先定義起來,寫起來會像這樣type Person = number | string
,未來在使用它時,便能變成age : Person
,會跟age: number | string
一樣意思,不過字面上又多了更多資訊,更好判讀了。
除了型別別名能這樣做,TypeScript還有一個很像的概念,叫做介面Interface,他會長這樣:
type Person = {
name: string
age: number
}
interface Person {
name: string
age: number
}
如果出現了同名的interface,只要他裡面的屬性名稱沒有衝突,便會直接跟先前的interface重合,我們也能透過關鍵字extends
擴充。使用型別別名時要擴充也是可以的,不過是用"&"
關鍵字,而非extends
。type跟interface是可以混用的,
interface Name {
name: string
};
interface Age {
age: number
};
type Person = Name & Age;
let guy: Person = {
name: "John",
age: 18
}
但如果可以的話,建議從頭到尾使用同一種寫法,比較一致跟好理解,網路上比較推薦的都是使用interface居多。
我們有時候可能不確定interface/type當中會有哪些屬性,所以可以透過問號"?"
來將某些屬性設為選擇性的,從下面例子來看,若將函式的參數設為FriedRice
這個型別,就不一定要提供ingredients
,但函式內要寫好"若ingredients
不存在"的判斷式。如果有些屬性你希望我們後續只能讀取他,而不允許修改它,則可以加上readonly
這個關鍵字,但如果使用了readonly
關鍵字的那個屬性是個物件,其內部還是可以被修改的,就跟使用const來宣告物件一樣道理。
interface FriedRice {
readonly name: string
price: number
ingredients?: string[]
}
小結:
花了一天的頁面來複習前面所提到的內容,如果發現對任何地方不熟悉,就要趕快往前翻,或者趕快去看官方文件了!
明天要講什麼還沒想到 0rz,只能先預祝大家週一順利了!!