【TypeScript 30】Day 1：型別推論及註記
Tags: webdev, typescript
Published: 2022-11-06 12:41:32


## 【TypeScript 30】Day 1：型別推論及註記

![](https://cdn-images-1.medium.com/max/2000/1*w3pX-UxawOa8Xdktu3mGPg.png)

距離上次寫文章已經相隔了四個月，在這四個月期間大量練習了 React、node.js、Boostrap 等等技術，也大致上掌握以上技術的核心，也因此都沒有好好沉澱寫文章，最近在學習 Next.js、typeScript 以及 tailwindcss，因為對於 React、Boostrap 都蠻熟習的，因此在學習 Next.js 和 tailwind 上 gap 比較小，相對的 typeScript 就比較需要花時間研究，接下來就聊聊 typeScript 吧!!!

最一開始來建置 ts 專案，因為平常習慣使用 React 開發，就使用方便的 create react app 吧。

### Insatll

    npx create-react-app ts-30 --template typescript

或

    yarn create react-app ts-30 --template typescript

安裝完後，緊接著安裝使用 typeScript 時所需要的套件，一樣可以選擇使用 npm 或是 yarn

### Install

    npm install — save typescript @types/node @types/react @types/react-dom @types/jest

或

    yarn add typescript @types/node @types/react @types/react-dom @types/jest

接著在專案中安裝 eslint 以及 prettier 模組

    yarn add prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-react-hooks

再加入設定檔於 /src 底下，專案建立完成後，先來釐清最基本的觀念【型別推論及註記】

![](https://cdn-images-1.medium.com/max/3798/1*d8hhe2ZeVR87pGtfkFpCtQ.png)
> # **【型別推論**（Inference）**及註記**（Annotation）**】**

下面就要來介紹個別的原理以及使用時機

**型別大致上分為幾種**

**原始型別 Primitive Types ：**number, string, boolean, undefined, null ES6 的 symbol與時常會在**函式型別**裡看到的 void皆屬於**原始型別。**

**物件型別 Object Types：**這些型別的共同特徵是 — — ***從原始型別或物件型別組合出來的複合型態***（比如物件裡面的 Key-Value 個別是 string 和 number 型別組合成)

* 基礎物件型別：JSON 物件，陣列，類別以及類別產出的物件（也就是 Class 以及藉由 Class *new* 出來的 Instance）

* TypeScript 擴充型別：Enum、Tuple(皆內建於 TS )

* 函式型別 Function Type：型別樣貌像是 ( input ) => ( output )

**明文型別 Literal Type：**值的本身也可以成為一個型別。如下圖，常數 string 被賦值 ' hello ts ' ，直接被宣告為 string 類型。

![](https://cdn-images-1.medium.com/max/2120/1*0sR2Ho8B3k1DKxF-2O5nHg.png)

**特殊型別**：參考的部落格作者所細分出的型別，即 any、never即以及 unknow ，這三種看起來都是沒有被定義型別所衍生出的型別，後面會介紹他們的差異。

**複合型別**：同上，即 union 與 intersection 的型別組合，但是跟其他的型別的差別在於：這類型的型別都是由邏輯運算子組成，分別為 | 與 &。
>  大致上將 typeScript 的型別多分類後，我們先回到最一開始要介紹的主題：型別推論及註記的原理以及使用時機

**型別推論：**還記得上面的這張圖嗎 ? 完美的展示了型別推論，typeScript 會**自動幫你推論型別。**

![](https://cdn-images-1.medium.com/max/2120/1*0sR2Ho8B3k1DKxF-2O5nHg.png)

不過讀者也許會思考那這樣 ts 和 js 的寫法哪裡不一樣了呢 ? 在這個案例中 typeScript 的確是幫我們把型別定義好，不過在更多情況下 ts 會把型別定義為 any那既然都已經使用 typeScript 開發了，當然還是不要讓型別是 any 的情況發生。

如下圖，如果把 nothing 這個變數設置為undefiend數就會被定義為 any ，正常情況下這邊並不會爆出錯誤，不過因為已經使用了 eslint ，所以這個錯誤直接被 eslint 判斷出來。

![](https://cdn-images-1.medium.com/max/2416/1*etkejYB0wWeoa7T3z6vuAw.png)

* *此類 null 跟 undefined 稱為 **Nullable Types **這類 **Nullable Types **會被推論為 any。*

不過，型別推論的本意並不是在這裡，而是當變數已經被推論過後，ts 就會跳出警告 — 不能再更改其型別。

拿上面 string這個變數例子，如果重新賦值該變數為數字型別的 22 ts 的型別小蚯蚓就跑出來了。

![](https://cdn-images-1.medium.com/max/2580/1*9ahlvRtPgAotKvuDPiVJ3w.png)

TypeScript 就會提出這個變數應該是 string 類型這個質疑。

![](https://cdn-images-1.medium.com/max/2934/1*8eXqF43l_pkX7oBpK9Dw4A.png)

那在這邊我們回到剛剛所提到的 ***Nullable Types ***測試看看，並不會跳出紅色蚯蚓。

![](https://cdn-images-1.medium.com/max/2220/1*wF8wk-skyi_LvOzmFYyyRg.png)

檢查完型別後會發現是 any，結論就是當型別被定義為 any 時某種程度上就已經失去使用 TypeScript 幫我們監督程式碼的本意了，因此在大多情況之下，盡量避免讓型別是 any 。

![](https://cdn-images-1.medium.com/max/2000/1*URjYXel1jX6-Y1nxOVR_Hw.png)

### 遲滯性初始 Delayed Initialization

除了沒有定義好型別之外，還有一種狀況會出現 any，就和原生的 JS 類似，當今天先定義了變數後，不直接指派值，**而是程式碼執行到後面才賦值**

![](https://cdn-images-1.medium.com/max/2220/1*dKewmiacsBuRR5Dr4Vf_MA.png)

在這邊 TS 已經把 testVariables 這個變數的型別認定為 any ，因此後面無論賦值是字串或是數字，TypeScript 都並不會檢查出錯誤。

![](https://cdn-images-1.medium.com/max/2000/1*_8Pc-CeByEbJPNiCdn2NCA.png)

概念其實就與我們剛剛提到的 ***Nullable Types ***有關，當只有宣告變數而沒有給他值時，該值自然就是 ***Nullable Types ***的 undefined，也因此型別就等於是 any 。

![](https://cdn-images-1.medium.com/max/2000/1*035h5HT-WGh3GgBjnN8-Pg.png)
>  如何避免 any 的型態出現 — — 型別註記 ( Type Annotation )

我們將 absoluteNothingVariables 和 absoluteNullVariables註記型別後再重新賦值

![](https://cdn-images-1.medium.com/max/2220/1*iwh2Y__g36dBvbq37FbR9Q.png)

TypeScript 就會把錯誤拋出來了

![](https://cdn-images-1.medium.com/max/2000/1*y6V9vIURx51I7Ljz7aL6Vw.png)

再測試看看

![](https://cdn-images-1.medium.com/max/2220/1*intKOxR1OLRLnxxSCnLhGA.png)

可以發現，經過型別註記後重新賦與註記的型別 ( 這裡是 string ) 後，接下來重新賦予 string 類型的值並不會出錯，不過一但清除為 undefined 或 null 就跳出錯誤，原因是： TS 已經將 stringVariable認定為 string 類型。

![](https://cdn-images-1.medium.com/max/2000/1*AW9xR3cqdX225MuzrO7FRQ.png)

不過如果還沒指派值之前就使用該變數，那這個 stringVariable雖然已經進行型別註記了，不過該值也還是 undefiend 不是嗎 ?

來嘗試看看，就像這樣，在annotationString 還沒賦值的情況之下，先使用了該變數

![](https://cdn-images-1.medium.com/max/2000/1*xPKcrt55xw4J1RayGfdofg.png)

跳出的錯誤非常明顯指出不能在還沒賦值時就使用該變數，有寫過 JS 的讀者應該對於這個問題比較熟悉，概念有點像 TDZ （Temporal Dead Zone，暫時性死區）這個討論範圍就延伸到了 JavaScript 的作用域，因此就不在這裡多做討論了。

![](https://cdn-images-1.medium.com/max/2000/1*4tIiccFUClap4Vb7Isq0ZQ.png)

小結：起初想說 TypeScript 不就只是加入型別而已沒甚麼，聽到大家說TypeScript 學習門檻很高，不太相信，不過經過這兩到三天的整理發現好像其實並不只是單純的加入型別這樣而已，剛開始就冷汗直流，希望能夠讓整理文章的速度再快一點。

參考資料：
[**Day 02. 前線維護・型別推論 X 註記 - Type Inference & Annotation - iT 邦幫忙::一起幫忙解決難題，拯救 IT 人的一天**](https://ithelp.ithome.com.tw/articles/10214719)
[**Adding TypeScript | Create React App**](https://create-react-app.dev/docs/adding-typescript/)
[**HiSKIO 專業技能線上學習平台 | 來自全球高品質的職場專業課程**](https://hiskio.com/account/courses?tab=completed&type=course)