iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 10
1
自我挑戰組

Typescript 初心者手札系列 第 10

【Day 10】TypeScript 資料型別 - 陣列型別(Array Types)-(上)

閱讀今天的文章前,先回顧一下昨天的學習,回答看看:

  • 函式回傳值註記為 void 和 undefined 有什麼差異呢?
  • 什麼是重載(overloads)?

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

今天我們要來討論陣列的型別推論,型別註記機制就留待明天下集待續。

陣列型別推論

陣列屬於物件型別(Object Types)的一種(可以翻看Day05的型別總覽複習一下所有資料型別)。在 JS 中,「陣列」是由相同類型的元素(element)的集合所組成的「有序的」資料結構,而陣列裡的資料型別不限,要放數字、字串、陣列甚至是物件都可以。

簡單型別陣列

讓我們先從簡單型別,像是:數字或字串開始來探討吧!首先,在預設設定下先宣告陣列

//1. 宣告空陣列
let foo = []

// 變數 = [ 元素, 元素, 元素, ]
// 2. 全部都是數字
let numbers = [1, 2, 3, 4, 5];

// 3.全部都是字串
let strings = ['hi', 'hello', 'world'];

這時候, TS 推論出來的型別分別是:
=> 1. 型別推論為 any[]

=> 2. 型別推論為 number[]

=> 3. 型別推論為 string[]

由此我們可以推論出 TS 會把這種元素全為同一簡單型別 T 的陣列用 T[] 的方式表示

這時候,如果對簡單型別陣列進行操作會發生什麼事呢?

let strings = ['hi','hello','world']

//對陣列任一值進行覆寫
strings[1] = '123' // 型別一致,Pass
strings[1] = 123   // 型別不一致,報錯

//使用陣列方法增加元素
strings.push('123') // 型別一致,Pass
strings.push(123)   // 型別不一致,報錯

//對整個陣列進行覆蓋
strings = ['hello','Kira']  // 型別一致,即使元素個數少了,仍然Pass
strings = ['hello','Kira','Una','Lin'] // 型別一致,即使元素個數多了,仍然Pass
strings = ['hello','Kira',undefined,,null] // undefined 和 null 屬於任何型別的值,空格為 undefined,Pass
strings = [123,456]   // 型別不一致,報錯

由此可知對同元素型別的陣列操作,只要元素的資料型別相同,例如:數字陣列number[]、布林值陣列boolean[],TS 都會通過編譯,而在預設設定下 undefined 和 null 可以作為所有資料型別的的子型別。但如果 strictNullChecks 打開,那 TS 就會報錯。

來個重點整理吧:簡單型別陣列的型別推論機制

同種元素皆為同型別 T 的陣列型別推論為 T[]

補充:在宣告空陣列let foo = []時,發生了有趣的事,在預設情況下 foo 會被推論為 any[],但是當打開 strictNullChecks 時,foo 就被推論為 never[],原本百思不得其解,直到找到stackoverflow這篇文章才得到解答。TS 團隊解釋說strictNullChecks 通常預期會和 noImplicitAny 一起使用,因此將推論 never[] 型別擴展至 any[]沒有意義,保留較具體的never[]比較好,倘若陣列需要操作開發者也會因此進行型別註記。TS 團隊真是用心良苦~

混合型別陣列

那如果陣列內的元素型別並非都是同一種資料型別呢?以下面的程式碼來說,combine 這個陣列混合了字串和數字,combine2 為有數字元素的稀疏陣列,而 combine3 陣列混合了數字、字串和布林值,那麼 TS 的型別推論結果如何呢?

//1. 元素包含數字和字串
let combine = [1, 'hi', 'hello', 2 ,'world'];

//2. 稀疏陣列(逗號間沒有值)
let combine2 =[1,,]

//3. 元素包含數字、字串和布林值
let combine3 = [1, 'hi', 'hello', 2 , true , 'world'];

  1. TS 針對混合型態的陣列推論結果為 (string | number)[]

  1. 逗號間的空值被視為 undefined,陣列推論結果為(number | undefined)[]

  1. 陣列推論結果為(string | number | boolean)[]

從上面的結果可以發現一個規律,TS 對於非同一資料型別的陣列,進行型別推論時會用聯合型別(union)來處理,union 類似於資料型別的 OR 或聯集,後面的文章會再仔細介紹。

來個重點整理吧:混合陣列的型別推論機制

(陣列集合裡所有型別 union 的結果)[]

物件元素陣列

剛剛我們上面所提到的陣列元素都是原始型別,那如果陣列元素是物件型別(包含函式、物件等)呢?一樣來試試看吧!

//1. 元素為同型別同屬性的物件
let objArr = [
  {lastname: 'Yang', firstname: 'Kira'},
  {lastname: 'Lin', firstname:'Una'}
]
//2. 元素為輸入與輸出型別相同的函式
let FunArr = [
  function add(num1:number,num2:number){return num1 + num2}
  function substract(num1:number,num2:number){return num1 - num2}
]
//3. 元素為不同型別陣列的陣列
let arraysArray = [
 [1,2,3],
 ['Hi','Hello'],
 [true, false],
]

  1. 型別推論出來的結果是 ({ lastname: string,firstname:string })[] 型別


2. 型別推論的結果是 ((number, number) => number)[] 型別


3. 推論結果為 (number[] | string[] | boolean[])[]

這裡可以看得出來,物件元素陣列仍然可以套用剛剛混合型別的規則,型別推論是集合陣列的所有型別union的結果[]。

小結

今天大致討論了 TS 陣列型別推論的邏輯,明天接著繼續討論 TS 陣列的型別註記囉!掰掰~


上一篇
【Day 09】TypeScript 資料型別 - 函式型別(Function Types)-(下)
下一篇
【Day 11】TypeScript 資料型別 - 陣列型別(Array Types)-(下)
系列文
Typescript 初心者手札30

尚未有邦友留言

立即登入留言