閱讀今天的文章前,先回顧一下昨天的學習,回答看看:
- 函式回傳值註記為 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'];
從上面的結果可以發現一個規律,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],
]
2. 型別推論的結果是 ((number, number) => number)[] 型別
3. 推論結果為 (number[] | string[] | boolean[])[]
這裡可以看得出來,物件元素陣列仍然可以套用剛剛混合型別的規則,型別推論是集合陣列的所有型別union的結果[]。
今天大致討論了 TS 陣列型別推論的邏輯,明天接著繼續討論 TS 陣列的型別註記囉!掰掰~