iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 4
2
自我挑戰組

Typescript 初心者手札系列 第 4

【Day 04】 TypeScript 判斷資料型別的機制 - 型別推論 x 斷言 x 註解

  • 分享至 

  • xImage
  •  

前言

前兩天介紹了如何建立 TS 專案的運行環境,接下來的幾天要近一步深入探討 TS 的型別系統了。在討論之前,讓我們先來回顧一下昨天的學習,試著回答看看:

  • 要透過哪個檔案進行客製化的編譯設定呢?
  • compilerOptions、files、include 和 exclude屬性分別是做什麼用的?

如果不清楚答案的話,可以看看 Day03 的文章喔!

TypeScript 型別系統

還記得第一天介紹什麼是 TS 時,有提到 TS 有點類似「擴充板的 JS 」,簡單來說,TS 就像是帶有型別系統的 JS,可以編譯成不同版本的 JS。當然,TS 和 JS 的差異不僅僅如此,但現在我們只專注討論「型別系統」這部分。

型別系統有許多優點,最重要的便是能夠在撰寫程式碼時考量靜態型別,當型別有誤就會立刻報錯,讓開發者在開發階段就能避免掉許多無預期的bugs。

以上面的程式碼為例,宣告一個變數 a ,值為字串 123,而後重新賦值為 5,但這時候,TS 判斷變數 a 的型別是字串,不能賦值數字 5,兩者型別不一致,此時,就會報錯(a 下方會有紅色底線~)。

等等,那 TS 怎麼判斷變數的資料型別呢?

事實上,在 TS 中有三種方式讓編譯器解析判斷資料型別,分別是型別推論(Type Inference)、型別斷言(Type Assertion)和型別註記(Type Annotation)。

型別推論(Type Inference)

在 TS 中,倘若沒有明確註記資料型別,聰明的 TS 編譯器便會自動推論出資料型別啦。如
下方的程式碼,宣告一個變數 a ,值為'123'

這時候,將滑鼠停在變數 a 上,此時,編輯器已經告訴我它是字串型別,但其實,我們完全沒有提到變數 a 的資料類型,變數a是字串型別是 TS 編譯器自動從程式碼中推論出來的,推論出型別後,當我們嘗試以非字串的資料賦值時就會報錯。

型別斷言(Type Assertion)

事實上,開發者比 TS 更了解編寫的程式碼。因此,TS 允許開發者覆蓋它的推論,這樣的機制稱為「型別斷言」。編譯器會接受開發者手動寫下的斷言,並且不會再送出警告錯誤。

型別斷言有點類似其他強語言如Java中的型別轉換(Type casting),但在TS中它只是編譯階段的功能而已。

語法

型別斷言有兩種寫法:

第一種是<型別>值 (angle-bracket <>)寫法

let code: any = 123; 
let employeeCode = <number> code; 

第二種則是值 as 型別 (as keyword)寫法

let code: any = 123; 
let employeeCode = code as number;

註:兩者寫法效果相同,只是若是開發 React 專案使用 JSX 語法時只能用第二種。

透過TypeScript Deep DiveTypeScript 入門教程的範例,來看看型別斷言的使用情境吧!

使用情境

情境1. JS 程式碼轉換成 TS 程式碼

const foo = {}
foo.bar = 123     // Error: property 'bar' does not exist on `{}`
foo.bas = 'hello' // Error: property 'bas' does not exist on `{}`

我們將上方的 JS 程式碼放進 TS 中會出現錯誤,因為 foo 的型別推論是沒有任何屬性的物件,因此,不能在屬性上添加 bar 或 bas ,這時候透過型別斷言可以避免這個問題

interface Foo {
  bar: number;
  bas: string;
}

const foo = {} as Foo;
或是也可以寫成
const foo = <Foo> {}

foo.bar = 123;     //OK
foo.bas = 'hello'; //OK

這時候,添加 bar 和 bas 屬性就不會報錯了。

情境2. 將聯合型別的變數指定為更加具體的型別

TS 有一種型別為聯合型別(細節後面的文章會再介紹),表示值可以是多種型別的一種。下方的程式碼表示 getLength 這個函式的參數可能為字串或數字的其中一種,然而 length 不是字串和數字的的共同属性,所以會報錯。

function getLength(something: string | number): number {
    if (something.length) {
        return something.length;
    } else {
        return something.toString().length;
    }
}

// error TS2339: Property 'length' does not exist on type 'string | number'.
//   Property 'length' does not exist on type 'number'.

這時候就可以使用「型別斷言」將 something 斷言為字串(string)

function getLength(something: string | number): number {
    if ((<string>something).length) {
        return (<string>something).length;
    } else {
        return something.toString().length;
    }
}

如此,something.length 就不會報錯了!

值得注意的是,TS 的型別斷言並非型別轉換(Type casting),雖然上面有講到,型別斷言有點類似其他強語言如Java中的型別轉換,但實際上仍是不同的。斷言聯合型別中不存在的型別是不行的。

function toBoolean(something: string | number): boolean {
    return <boolean>something;
}

// index.ts(2,10): error TS2352: Type 'string | number' cannot be converted to type 'boolean'.
//   Type 'number' is not comparable to type 'boolean'.

型別註解(Type Annotation)

TS 讓開發者可以透過手動註解的方式,明確宣告資料型別,告訴編譯器必須符合註解的型別,以方便在開發時就抓到變數的錯誤賦值問題。

語法

在做型別註解時,會在變數、參數或屬性後面加上冒號:型別,冒號後方可加一個空格,例如:

//變數的型別註解
const age: number = 32

//函式參數的型別註解
function display(name: string){
    console.log(name);
}

//函式參數/回傳值的型別註解
function display(a: number,b: number): number{
    return a + b
}

宣告型別之後,就不能使用其他型別的資料進行賦值,否則會報錯。

補充:型別註解 v.s 型別斷言的比較
在研究型別註解和斷言的時候,對於兩者的具體使用情境其實有點模糊。在和工作室的大神請教後,整理我所理解兩者的使用差異如下:

  1. 大部分情況下會使用型別註解 ; 型別斷言使用情境較少。
  2. 型別註解告訴編譯器這個資料必須永遠都是這個型別 ; 而型別斷言則主要用在覆蓋 TS 編譯器自動進行的型別推斷和型別相容性規則(Type Compatibility),告訴編譯器你知道這個值要符合斷言的型別,從而避免了上面範例中型別不兼容的錯誤產生。關於兼容性的討論在stackoverflow看到不錯的討論。
  3. 型別註解大部分使用在初始化階段,像是宣告變數、函式參數或回傳值型別等 ; 而型別斷言可能用在接收外部參數,中間過程需要明確指定資料型別的時候。

小結

今天初步介紹了 TS 的型別系統、TS 判別資料型別的三種方式 - 型別推論(Type Inference)、型別斷言(Type Assertion)和型別註解(Type Annotation)以及撰寫語法。

簡單來說,型別推論就是在没有明確指定型別的情況下,由 TS 自動判斷型別 ; 型別斷言則是手動指定資料型別,可以覆蓋掉 TS 自動推論的資料型別 ; 型別註解則是明確指定資料型別,三者的使用情境不太相同,各種使用情境後面的文章再來細細探討。

明天終於可以來介紹 TS 各種資料型別了!Ya ~

如有錯誤的地方,還請留言告知,我會盡快修改調整,感謝:)

參考資料:
TypeScript Deep Drive
TypeScript 官網
Poetry Blog
TypeScript入門教程
TypeScript cheatsheat


上一篇
【Day 03】 TypeScript 編譯設定 - tsconfig.json
下一篇
【Day 05】TypeScript 資料型別 - 總覽
系列文
Typescript 初心者手札30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
ytyubox
iT邦新手 5 級 ‧ 2019-09-19 12:20:30

syntax highlight 有部分沒有統一, 是不是因為混用 ts 與 js

Kira iT邦新手 5 級 ‧ 2019-09-20 17:09:17 檢舉

已調整:)

我要留言

立即登入留言