iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0
Modern Web

TypeScript 魔法 - 喚醒你的程式碼靈感系列 第 17

Day17 - 相信我,我知道這個值的型別是什麼 - 型別斷言(Type Assertions)

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20231002/20152047M63vUBNgOM.png

什麼是型別斷言?

型別斷言,也可以稱為型別轉換,是一種告訴 TypeScript 編譯器「相信我,我知道這個值的型別是什麼」的機制。它類似於其他程式語言中的型別轉換,但在 TypeScript 中,它更為安全,因為它僅在編譯時起作用,不影響運行時的行為。

如何使用型別斷言?

  1. 使用 as 關鍵字
  2. 使用尖括號 <>

特別注意:尖括號 <> 在 React 中也存在類似的語法,為了避免與 JSX 語法產生衝突,TypeScript 提供的替代方式是使用 {}as 一起,以確保 JSX 語法和型別斷言不會衝突。

非空斷言運算子

非空斷言運算子是 TypeScript 中的一個特殊運算子,它用來告訴 TypeScript 我們確信某個表達式的值不會是 nullundefined。這個運算子以 ! 表示。

未使用 !

function numberTransformString(value: number | null) {
  console.log(value.toString()); // TypeScript 報錯,'value' 可能為「null」或「未定義」。
}

numberTransformString(123);

使用 !

function numberTransformString(value: number | null) {
  console.log(value!.toString()); // 通過,字串型別的 123
}

numberTransformString(123);

型別斷言在 DOM 不同選取方法的型別差異

HTML 現在包含一個 <p> 標籤。接著,我們使用選擇器來抓取這個 <p> 標籤的 DOM 元素,將它賦值給了 paragraph 變數。當我們將滑鼠懸停在 paragraph 變數上時,我們可以觀察到 TypeScript 推斷出 paragraph 的型別可能是 HTMLParagraphElementnull。這是因為在頁面中,這個元素可能不存在;值得注意的是,TypeScript 實際上能夠辨識出這是一個段落元素

<p>一起學習 TypeScript 吧!</p>
const paragraph = document.querySelector('p');

https://ithelp.ithome.com.tw/upload/images/20231002/20152047a36cCDuekb.png

如果我們在 <p> 標籤上加上了一個 id 屬性,然後使用 getElementById 函式來選取該元素,我們會注意到 TypeScript 推斷這個元素只是一個 HTMLElementnull,而不清楚具體是哪種 HTML 元素。這是因為 TypeScript 不會深入分析我們的 HTML,只知道它是某種 HTML 元素

<p id="message-output">一起學習 TypeScript 吧!</p>
const paragraph = document.getElementById('message-output');

https://ithelp.ithome.com.tw/upload/images/20231002/20152047Fn2RfZ7X70.png

使用型別斷言處理 DOM 元素

HTML 現在包含一個 <input> 標籤。接著,我們嘗試抓取這個 DOM 元素並嘗試賦值給它,但注意到 TypeScript 產生了一些錯誤。

<input type="text" id="user-input" />
const userInput = document.getElementById('user-input');

userInput.value = '我是肉鬆'; // TypeScript 報錯
// 錯誤一: 'userInput' 可能是 'null'
// 錯誤二: 類型 'HTMLElement' 沒有屬性 'value'
  • 錯誤一:在 TypeScript 中,當我們使用 getElementById 這樣的 DOM 選取方法時,它們的返回型別通常是 HTMLElementnull。這是因為根據指定的 id 可能找不到對應的元素,因此返回值可以是 null
  • 錯誤二:TypeScript 認為 userInput 變數的型別是 HTMLElement,而 HTMLElement 並不一定具有 value 屬性。為了解決這個問題,需要使用型別斷言,明確告訴 TypeScript userInput 的型別是 HTMLInputElement,因為我們知道它實際上是一個 input 元素。

解決方法一:使用 ! 搭配尖括號 <>

const userInput = <HTMLInputElement>document.getElementById('user-input')!;

userInput.value = '我是肉鬆'; 

解決方法二:使用 ! 搭配 as 關鍵字。

const userInput = document.getElementById('user-input')! as HTMLInputElement;

userInput.value = '我是肉鬆';

解決方法三:使用條件判斷式搭配 as 關鍵字。

const userInput = document.getElementById('user-input');

if (userInput) {
  (userInput as HTMLInputElement).value = '我是肉鬆';
}

特別注意:要將表達式用括號 () 括起來,確保先計算表達式,然後才嘗試存取這個表達式結果的值。這是確保型別斷言正確運作的重要步驟。

本日重點

  1. 型別斷言可以使用 as 關鍵字或尖括號 <> 來執行。
  2. 不同的 DOM 選取方法可能會返回不同的型別。
  3. 使用 ! 來告訴 TypeScript 忽略可能的 nullundefined
  4. 在存取可能為 nullundefined 的屬性或方法之前,請使用括號 () 將表達式括起來,以確保型別斷言正確運作。

參考


上一篇
Day16 - TypeScript 的黑洞 - 絕不(never)
下一篇
Day18 - 型別融合 - 交集型別(Intersection Types)
系列文
TypeScript 魔法 - 喚醒你的程式碼靈感30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言