TypeScript 遵守每個陣列保持單一資料型別,最佳作法是記住陣列中最初的資料型別,並且只允許陣列對此種數據資料進行操作。
陣列型別
例如以下:
// arrayOfNumbers 是一個存「數值」資料的「陣列」。
let arrayOfNumbers: number[];
arrayOfNumbers = [4, 8, 15];
註:陣列型別也可以使用類似 Array<number> 的語法來撰寫,稱為泛型類別(class generics),大多數開發人員偏好更簡易的 number[] 語法。
陣列與函數型別
例如以下:
// createStrings 變數是一個函式,會回傳資料為字串的陣列
let createStrings: () => string[];
// stringCreators 變數會是一個陣列,陣列當中的每個資料,會是一個回傳字串的函式
let stringCreators: (() => string)[];
聯集的陣列型別
可以使用聯集型別來表示陣列中每個元素是不同的型別。仍需要使用中括號來註記陣列的內容部分與聯集型別。
例如以下:
// 型別是 number 或 string 的陣列
let stringOrArrayOfNumbers: string | number[];
// 型別是一個陣列,陣列裡的資料會是 number 或 string
let arrayOfStringOrNumbers: (string | number)[];
另外,從陣列中的資料,也可推斷所有可能型別的聯集,例:
// 型別為 (string | undefined)[]
const namesMaybe = ["aaa", "bbb", undefined];
演變成 any 的陣列
如果沒有在最初空陣列上,為變數設定所包含的型別註記,TypeScript 會將陣列視為演變的 any[],這代表可以接收任何內容,我們並不喜歡 any[] 陣列,因為這會允許增加不確定的資料,進而否定了 TypeScript 型別檢查的優點。例如:
// 此時型別為 any[]
let values = [];
// 此時型別變為 string[]
values.push("");
// 此時型別再變為 (number | string)[]
values[0] = 0;
多維陣列
二維陣列或陣列的陣列,將有兩個中括號([]) 符號:
let arrayOfArraysOfNumbers: number[][];
// 上面這行,也可以寫這樣:
let arrayOfArraysOfNumbers: (number[])[];
arrayOfArraysOfNumbers = [
[1, 2, 3],
[2, 4, 6],
[3, 6, 9]
];
一個三維度陣列,將具有三個中括號([]),以此類推。
陣列成員
第一個例子:檢索陣列成員並回傳該陣列型別的元素,以下 defenders 陣列的型別是 string[],所以 defender 是一個字串:
// defenders 的型別是 string[]
const defenders = ["a", "b"];
// defender 的型別是 string
const defender = defenders[0];
第二個例子:聯集型別陣列的成員,本身就是相同的型別:
// abc 的型別為 (string | Date)[]
const abc = ["a", new Date(1999, 1, 2)];
// a 的型別會是 Date | string
const a = abc[0];
展開和剩餘
展開
陣列可以使用 … 展開運算符號。TypeScript 分析陣列結果,將可以包含來自任何陣列輸入的資料。
例如以下:已知聯集陣列包含 string 型別和 number 型別的資料,因此推斷其型別為 (string | number)[]:
// 型別為 string[]
const soldiers = ["a", "b", "c"];
// 型別為 number[]
const soldierAges = [1, 2, 3];
// 型別為 (string | number)[]
const conjoined = [...soldiers, ...soldierAges];
剩餘參數
例如以下: …names 參數只接受 string 資料的陣列。
// names 的型別為 string[]
function logWarriors(greeting: string, ...names: string[]){
for(const name of names){
console.log(greeting + ", " + name + "!");
}
}
const warriors = ["a", "b", "c"];
logWarriors("Hello", ...warriors); // 允許
const birthYears = [1, 2, 3];
// 以下會報錯:型別 number 的引數不可指派給型別 string 的參數
logWarriors("Born in", ...bithYears);
元組
理論上,JavaScript 陣列可以是任意大小,但是,若可以使用固定大小的陣列,有時會較好,所以這種固定大小的陣列,就稱為元組(tuple)。
元組陣列在每個索引位置都有一個特定的已知型別,它可能比陣列所有可能成員的聯集型別更加具體化。
宣告元組型別的語法,看起來像一個陣列字面值,但使用型別代替了元素值。
例:
// 宣告為一個元組型別,索引值為 0 是 number,索引值為 1 是 string
let yearAndWarrior: [number, string];
yearAndWarrior = [50, "a"]; // 允許
// 以下會報錯,型別 boolean 不可指派給型別 number
yearAndWarrior = [false, "b"];
// 以下會報錯,型別 [number] 不可指派給型別 [number, string],來源有一個元素,但目標需要 2 個。
yearAndWarrior = [530];
元組通常與 JavaScript 中的解構一起使用,才能夠一次指派多個值,例:
// year 型別會推斷是 number
// warrior 型別會推斷是 string
let [year, warrior] = Math.random() > 0.5 ? [340, "a"] : [123, "b"];
元組的指派性
元組型別被 TypeScript 視為比可變長度陣列,更為具體化的型別。這表示可變長度陣列型別,不能指派給元組型別。
以下例子:
// 型別會推斷為 (boolean | number)[]
const pairData = [false, 123];
// 以下會報錯:型別 (boolean | number)[] 不可指派給型別 [boolean, number],目標需要2個元素,但來源的元素可能較少。
const pairTupleLoose: [boolean, number] = pairData;
再看下一個例子:
const tupleThree: [boolean, number, string] = [false, 123, "a"];
const tupleTwoExact: [boolean, number] = [tupleThree[0], tupleThree[1]]; // 允許
// 以下會報錯:型別 [boolean, number, string] 不可指派給型別 [boolean, number]
const tupleTwoExtra: [boolean, number] = tupleThree;
元組視為剩餘參數
因為元組具有長度資訊與元素型別,被視為更豐富且具體化型別資訊的陣列,所以對於要傳遞給函式的參數特別有用。
例:
function logPair(name: string, value: number) {
console.log(name + " has " + value);
}
const pairArray = ["a", 1];
// 會報錯:擴展引數必須具有元組型別或傳遞給剩餘參數
logPair(...pairArray);
const pairTupleIncorrect: [number, string] = [1, "b"];
// 會報錯:型別 number 的引數不可指派給型別 string 參數
logPair(...pairTupleIncorrect);
const pairTupleCorrect: [string, number] = ["c", 1];
logPair(...pairTupleCorrect); // 正確
另外一個例子,若想使用剩餘參數元組,可與陣列混合。例:
function logTrio(name: string, value: [number, boolean]) {
console.log(name + " has " + value[0] + " " + value[1]);
}
const trios: [string, [number, boolean]][] = [
["a", [1, true]],
["b", [2, false]],
["c", [3, false]]
];
trios.forEach(trio => logTrio(...trio)); // 正確
// 以下會報錯
trios.forEach(logTrio);
元組推導
TypeScript 通常將建立的陣列,視為可變長度陣列,而不是元組。如果解析到一個陣列,被使用作為變數的初始值或函式的回傳值,那麼將會假定成一個大小靈活的陣列,而非一個固定大小的元組。
例:
// 回傳型別:(string | number)[]
function firstCharAndSize(input: string) {
return [input[0], input.length];
}
// firstChar 型別:string | number
// size 型別:string | number
const [firstChar, size] = firstCharAndSize("a");
TypeScript 中有兩種常見的方式,用來指定一個變數,應該是一個更具體的元組型別,即:「明確的元組型別」、「常數斷言元組」。
明確的元組型別:
元組型別可以用在型別註記中,例如函式的回傳型別註記。如果函式被宣告為回傳元組型別,並且回傳陣列字面形式,則該陣列將被推斷為元組,例:
// 回傳型別:[string, number]
function firstTest(input: string): [string, number]{
return [input[0], input.length];
}
// firstChar 型別:string
// size 型別:number
const [firstChar, size] = firstTest("abc");
常數斷言元組:
在上述的明確型別中,註記輸入的元組型別會很麻煩。
TypeScript 提供一個 as const 運算符號作為替代方案,稱為常數斷言(const 斷言),原文為 const assertion,它可以放在資料之後。
const 斷言:告訴 TypeScript 在推斷其型別時,可能使用字面或唯讀的形式分析資料。如果將運算符號放在一個陣列之後,則表示該陣列應被視為一個元組,例:
// 型別:(string | number)[]
const unionArray = [1157, "abc"];
// 型別:readonly [123, "def"]
const readonlyTuple = [123, "def"] as const;
留意 as const 斷言,不僅僅將大小靈活的陣列切換到固定大小的元組:它們還向 TypeScript 表明該元組是唯讀的,不能期望在其他地方做資料的修改。例:
// 允許修改,因為具有傳統的明確元組型別
const pairMutable: [number, string] = [1157, "ab"];
pairMutable[0] = 1247; // 正確
const pairConst = [1157, "cd"] as const;
// 以下錯誤:Cannot assign to '0' because it is a read-only property.
pairConst[0] = 11;
另外,函式回傳的唯讀的 [string, number],但外部使用的程式碼只關心此回傳元組中的資料:
// 回傳型別:readonly [string, number]
function firstCharAsConst(input: string){
return [input[0], input.length] as const;
}
// firstChar 型別:string
// size 型別:number
const [firstChar, size] = firstCharAsConst("abc");
發佈留言
很抱歉,必須登入網站才能發佈留言。