TypeScript - Array

學習資源:

Codecademy

介紹

陣列與使用 primitive 型別有點不同。這是因為陣列通常包含許多資料。追蹤陣列的型別意味著跟蹤每個元素的型別。 例如:

let firstArray = [1, 2, 3, 4];
let secondArray =  [5, '6', [7]];

我們可以看到,firstArray的元素都是number 型別。 另一方面,secondArray具有不同型別的元素:number、string 和 array。兩者都是 JavaScript 陣列的例子。

以下的例子示範說,假如我們不用 TypeScript 的話要怎麼做這些型別的檢查:

let customersArray = ['Custy Stomer', 'C. Oostomar', 'C.U.S. Tomer', 3432434, 'Custo Mer', 'Custopher Ustomer', 3432435, 'Kasti Yastimeur'];

//Write Your Code here:
function checkCustomersArray(){
  for(let i=0; i<customersArray.length; i++){
      if(typeof customersArray[i] != 'string'){
        console.log(`Type error: ${customersArray[i]} should be a string!`);
      }
  }
}
checkCustomersArray()

function stringPush(val){
  if(typeof val === 'string'){
    customersArray.push(val);
  } 
}

Array Type Annotations 陣列註釋

陣列型別的型別註釋相當簡單:把 [] 放在元素型別之後。在以下的程式中,names 是一個只能包含字串的陣列:

let names: string[] = ['Danny', 'Samantha'];

另一種方法是使用 Array<T>,其中T代表型別。

let names: Array<string> = ['Danny', 'Samantha'];

而正如我們預期的那樣,如果我們嘗試將數字陣列 assign 給字串陣列的變數,就會收到型別錯誤的訊息:

let names: string[] = [1,2,3]; // Type Error!

而當陣列的方法有操作不同型別的元素時,也會跳出錯誤。

let names: string[] = ['Damien'];
names.push(666) // Type Error!

Multi-dimensional Arrays 多維陣列註釋

除了以上一維的陣列結構,事實上我們也可以做多維的陣列結構:

let arr: string[][] = [['str1', 'str2'], ['more', 'strings']];

空陣列 [] 與任何陣列型別相容:

let names: string[] = []; // No type errors.
let numbers: number[] = []; // No type errors.
names.push('Isabella');  
numbers.push(30);

Tuples 元組

如我們所知道的,JavaScript 陣列可以靈活的容納不同型別的元素。而使用TypeScript,我們還可以定義具有固定型別序列的陣列:

let ourTuple: [string, number, string, boolean] = ['Is', 7 , 'our favorite number?' , false];

在 TypeScript 中,當一個陣列用特定型別的元素進行型別時,被稱為元組。元組型別指定相容元組的長度和順序,如果不滿足以下任何一個條件,將導致錯誤:

let numbersTuple: [number, number, number] = [1,2,3,4]; // Type Error! numbersTuple should only have three elements.
let mixedTuple: [number, string, boolean] = ['hi', 3, true] // Type Error! The first elements should be a number, the second a string, and the third a boolean.

就 JavaScript 而言,元組就像陣列一樣。它們都有 .length 屬性。我們可以使用 [index] 訪問(或更改)兩者的元素。但是,儘管它們相似,但元組和陣列在TypeScript 中並沒有相容的型別。具體來說,我們無法為元組變數 assign 陣列元素,即使元素型別正確:

let tup: [string, string] = ['hi', 'bye'];
let arr: string[] = ['there','there'];
tup = ['there', 'there']; // No Errors.
tup = arr; // Type Error! An array cannot be assigned to a tuple.

Array Type Inference 陣列型別推論

TypeScript 可以從初始值和返回值中推斷變數型別。但即便如此,我們可能還不知道在處理陣列時到底要期待什麼來推論型別。

let examAnswers= [true, false, false];

它似乎同樣可能是 boolean[] or [boolean, boolean, boolean] 。實際上,他會是 boolean[]

examAnswers[3] = true; // No type error.

由於元組有固定長度,我們無法向元組新增額外的 boolean 元素:

let tupleOfExamAnswers: [boolean, boolean, boolean] = [true, false, false];
tupleOfExamAnswers[3] = true; // Type error! The tuple only has 3 elements.

而當我們使用 .concat() 方法時,我們也會得到同樣的型別推論:

let tup: [number, number, number] = [1,2,3];
let concatResult = tup.concat([4,5,6]); // concatResult has the value [1,2,3,4,5,6].

在上面的程式中,TypeScript 會將變數 concatResult 推斷為數字陣列,而不是元組。

Rest Parameters

將型別 assign 給 rest parameters 類似於將型別 assign 給陣列。以下是沒有型別的 rest parameters 範例:

function smush(firstString, ...otherStrings){
  let output = firstString;
  for(let i = 0; i < otherStrings.length; i++){
    output = output.concat(otherStrings[i]);
  }
  return output;
}

以下是上面程式的結果:

smush('a','h','h','H','H','H','!','!'); // Returns: 'ahhHHH!!'.

但就像大家知道的,這樣的方式並不安全,如果使用 smush(1,2,3) 的話,就會出錯。所以為了確保型別,可以向以下這樣:

function smush(firstString, ...otherStrings: string[]){
  /*rest of function*/
}

Spread Syntax

以下是一個沒有使用元組的應用:

function gpsNavigate(startLatitudeDegrees:number, startLatitudeMinutes:number, startNorthOrSouth:string, startLongitudeDegrees: number, startLongitudeMinutes: number, startEastOrWest:string, endLatitudeDegrees:number, endLatitudeMinutes:number , endNorthOrSouth:string, endLongitudeDegrees: number, endLongitudeMinutes: number,  endEastOrWest:string) {
  /* navigation subroutine here */
}
let codecademyCoordinates: [number, number, string, number, number, string] = [40, 43.2, 'N', 73, 59.8, 'W'];
let bermudaTCoordinates: [number, number, string, number, number, string] = [25, 0 , 'N' , 71, 0, 'W'];
gpsNavigate(...codecademyCoordinates, ...bermudaTCoordinates);
// And by the way, this makes the return trip really convenient to compute too:
gpsNavigate(...bermudaTCoordinates, ...codecademyCoordinates);
// If there is a return trip . . .