---
# System prepended metadata

title: 陣列應用

---

## 陣列應用
### 有指定型別的陣列

```ts=
function calculateTax(amount: number): number {
  return amount * 1.2;
}

function writePrice(product: string, price: number): void {
  console.log(`Price for ${product}: $${price.toFixed(2)}`);
}

// Before
let hatPrice = 100;
let glovesPrice = 75;
let umbrellaPrice = 42;
writePrice("Hat", calculateTax(hatPrice));
writePrice("Gloves", calculateTax(glovesPrice));
writePrice("Umbrella", calculateTax(umbrellaPrice));

// After
let price: number[] = [100, 75, 42]; //只能接受數值
let names: string[] = ["Hat", "Gloves", "Umbrella"]; //只能接受字串
writePrice(names[0], calculateTax(price[0]));
writePrice(names[1], calculateTax(price[1]));
writePrice(names[2], calculateTax(price[2]));
```
![](https://hackmd.io/_uploads/H11F6rHGa.png)


#### forEach
```ts=
forEach(function callback(currentValue[, index[, array]]))
```
##### 不使用類型推斷
```ts=
price.forEach((price: number, index: number) => {
  writePrice(names[index], calculateTax(price));
})
```
##### 使用型別推斷
```ts=
price.forEach((price, index) => {
  writePrice(names[index], calculateTax(price));
})

// declare let price: number[];
// declare let names: string[];
```
由於編譯器推斷方法是根據**陣列的初始元素來判斷**，混雜多型別的值會導致判斷錯誤
```ts=
let price: number[] = [100, 75, 42, "20"];
let names: string[] = ["Hat", "Gloves", "Umbrella", "Sunglasses"];

price.forEach((price, index) => {
  writePrice(names[index], calculateTax(price));
})
```
![](https://hackmd.io/_uploads/ryN5DUrGa.png)

##### 潛在問題一(應該預防空陣列)
```ts=
let price = [] //建立空陣列導致編譯器推論為any
price.push(...[100, 75, 42, "20"])
let names: string[] = ["Hat", "Gloves", "Umbrella", "Sunglasses"];

price.forEach((price, index) => {
  writePrice(names[index], calculateTax(price));
})
```
![](https://hackmd.io/_uploads/H1P8YLSMp.png)
還是可以如期印出數值，不會出錯
![](https://hackmd.io/_uploads/H1NoKLSza.png)

##### 潛在問題二(never型別)
````ts=
// tsconfig.json
{
  "compilerOptions": {
    "target": "es2020",
    "rootDir": "./src",
    "outDir": "./dist",
    "declaration": true,
    "strictNullChecks": true //嚴格禁止null與undefined 
                             //也禁止推論為any型別
  }
}
````
![](https://hackmd.io/_uploads/B1Lp5LrGa.png)
無法填入任何東西到這個陣列之中
![](https://hackmd.io/_uploads/Bkybo8rza.png)
如果在這情況下也可以操作成功 -> **幫空陣列加上斷言**
```ts=
let price = [] as number[]; //增加斷言
price.push(...[100, 75, 42, 20])
let names: string[] = ["Hat", "Gloves", "Umbrella", "Sunglasses"];

price.forEach((price, index) => {
  writePrice(names[index], calculateTax(price));
})
```

## tuple應用
### tuple是固定長度的陣列，但編譯成JS後，還是一般陣列
````ts=
function calculateTax(amount: number): number {
  return amount * 1.2;
}

function writePrice(product: string, price: number): void {
  console.log(`Price for ${product}: $${price.toFixed(2)}`);
}

let hat: [string, number] = ["Hat", 100];
let gloves: [string, number] = ["Gloves", 75];
writePrice(hat[0], calculateTax(hat[1]));
writePrice(gloves[0], calculateTax(gloves[1]));
````

![](https://hackmd.io/_uploads/SJmy6IBzp.png)

##### 處理tuple物件
```ts=
let hat: [string, number] = ["Hat", 100];

// 編譯器推斷
hat.forEach((h) => {
  if (typeof h === "string") { //型別防禦
    console.log(`String: ${h}`);
  } else {
    console.log(`Number: ${h}`);
  }
})

// 型別
hat.forEach((h: string|number) => {
  if (typeof h === "string") {
    console.log(`String: ${h}`);
  } else {
    console.log(`Number: ${h}`);
  }
})
```

##### tuple型別
tuple大雜燴 -> 使用防禦敘述
```ts=
let hat: [string, number] = ["Hat", 100];
let products: [string, number][] = [["Hat", 100], ["Gloves", 75]];
let tupleUnion: ([string, number] | boolean)[] = [true, false, hat, ...products];
tupleUnion.forEach((elem: [string, number] | boolean) => {
  if (elem instanceof Array) {
    console.log(`Array: ${elem}`);
  } else if (typeof elem === "boolean") {
    console.log(`Boolean: ${elem}`);
  }
})
```
由於typeof無法判斷值是否是tuple(在JS都是array) -> **instanceof**

##### tuple有標籤
```ts=
let hat: [product: string, price: number] = ["Hat", 100];
```
但不能使用標籤作為屬性來取直 -> 增加可讀性而已

## enum
### 介紹enum
enum: 使用一個名稱還存取一系列有特定名稱的嘗數值
![](https://hackmd.io/_uploads/r1HO-DHzT.png)

````ts=
function writePrice(product: string, price: number): void {
  console.log(`Price for ${product}: $${price.toFixed(2)}`);
}

enum Product { Hat, Gloves }
let products: [Product, number][] = [[Product.Hat, 100], [Product.Gloves, 75]];

products.forEach((prod: [Product, number]) => {
  switch (prod[0]) {
    case Product.Hat:
      writePrice("Hat", calculateTax(prod[1]));
      break;
    case Product.Gloves:
      writePrice("Gloves", calculateTax(prod[1]));
      break;
  }
})
````

### enum運作原理
```ts=
[Product.Hat, Product.Gloves].forEach(val => {
  console.log(`Number value: ${val}, type: ${typeof val}`);
})
```
![](https://hackmd.io/_uploads/BJx0XvSMa.png)
在JS內是屬於number

所以可以經由number找到對應的列舉值
```ts=
enum Product { Hat, Gloves }
let productValue: Product = 0
let productName: string = Product[productValue];
console.log(`Value: ${productValue}, Name: ${productName}`);

//Value: 0, Name: Hat
```
### 自定義列舉值
```ts=
enum Product { Hat, Gloves = 20, Umbrella }

//JS
declare enum Product {
    Hat = 0,
    Gloves = 20,
    Umbrella = 21
 }
```
如果使用不存在的值
```ts=
let productValue: Product = 19
let productName: string = Product[productValue];
console.log(`Value: ${productValue}, Name: ${productName}`);

// Value: 19, Name: undefined
```

### 字串列舉
```ts=
enum City { London = "LON", Paris = "PAR", NY = "NYC" }
console.log(`City: ${City.London}, type: ${typeof City.London}`);
console.log(`City: ${City.Paris}, type: ${typeof City.Paris}`);

// City: LON, type: string
// City: PAR, type: string
```
=> 增加可讀性而已

### 列舉的局限
1. 如果使用**number**作為列舉值的話 => 可能出現undefine
2. 但使用**字串**作為列舉值的話 => 則不存在的字串值則會在編譯時出錯
3. 列舉是透過JS的數值實作，所以typeof無法區分列舉以及數值的差別

### 常數列舉
使用物件可能會引響效率 => 使用常數
```ts=
enum Product { Hat, Gloves, Umbrella }
let productValue: Product = Product.Hat;
console.log(productValue)
```
```javascript=
var Product;
(function (Product) {
    Product[Product["Hat"] = 0] = "Hat";
    Product[Product["Gloves"] = 1] = "Gloves";
    Product[Product["Umbrella"] = 2] = "Umbrella";
})(Product || (Product = {}));
let productValue = Product.Hat;
console.log(productValue);
```
JS是產生一個Product物件，然後將物件的屬性值指派給productValue

如果加上const時
```javascript=
let productValue = 0 /* Hat */;
console.log(productValue);
productValue = 1 /* "Gloves" */;
console.log(productValue);
```
編譯器會找到列舉成員對應的值，然後在JS中將值指派給變數
但這樣就無法利用列舉值反查列舉名稱
```ts=
let productName: string = Product[2];
console.log(productName)

// src/index.ts(61,35): error TS2476: A const enum member can only be accessed using a string literal.
```

## 字面值型別
```ts=
let restrictedValues: 1 | 2 | 3 | 4 | 5 = 3
console.log(restrictedValues)

// 3
```
但如果是字面值以外的值
```ts=
let restrictedValues: 1 | 2 | 3 | 4 | 5 = 70
console.log(restrictedValues)

// src/index.ts(89,5): error TS2322: Type '70' is not assignable to type '1 | 2 | 3 | 4 | 5'.
```
交集
```ts=
let restrictedValues: 1 | 2 | 3 | 4 | 5 = 5
console.log(restrictedValues)

let secondValue: 1 | 10 | 100 | 1000 = 1
restrictedValues = secondValue
console.log(restrictedValues)

// 5
// 1

let secondValue: 1 | 10 | 100 | 1000 = 10
// src/index.ts(93,1): error TS2322: Type '10' is not assignable to type '1 | 2 | 3 | 4 | 5'.
```

### 應用在函式中
```ts=
function calculateTax(quantity: 1|2, price: number): number {
  return quantity * price
}

let total = calculateTax(2, 19.99)
console.log(`2 items purchased at $19.99, total: ${total}`)

// 2 items purchased at $19.99, total: 39.98
```

### 混合不同的字面型別值
```ts=
function getRandomValue(): 1 | 2 | 3 | 4 {
  return Math.floor(Math.random() * 3) + 1 as 1 | 2 | 3 | 4
}

enum City { London = "LON", Paris = "PAR", NY = "NYC" }
function getMixedValue(): City.London | 2 | "Hello" | true {
  switch (getRandomValue()) {
    case 2:
      return 2
    case 1:
      return City.London
    case 3:
      return "Hello"
    case 4:
      return true
  }
}

console.log(getMixedValue())
```

### 函式字面值型別多載
```ts=
enum City { London = "LON", Paris = "PAR", NY = "NYC" }
function getMixedValue2(input: 1): 1;
function getMixedValue2(input: 2|3): "Helo" | true;
function getMixedValue2(input: 4): City.London;

function getMixedValue2(input: number): number | string | boolean | City 
{
  switch (input) {
    case 1:
      return 1
    case 2:
      return "Hello"
    case 3:
      return true
    case 4:
    default:
      return City.London
  }
}

let first = getMixedValue2(1)
let second = getMixedValue2(2)
let third = getMixedValue2(4)
console.log(`First: ${first}, Second: ${second}, Third: ${third}`)

// First: 1, Second: Hello, Third: LON
```
```ts=
declare function getMixedValue2(input: 1): 1;
declare function getMixedValue2(input: 2 | 3): "Helo" | true;
declare function getMixedValue2(input: 4): City.London;
declare let first: 1;
declare let second: true | "Helo";
declare let third: City.London;
```

## 使用型別別名
```ts!
enum City { London = "LON", Paris = "PAR", NY = "NYC" }
type comboType = 1|2|3|City.London // 可以是四種可能
type comboTupleType = [string, number| boolean, comboType] // 由三種元素組成

let result: comboTupleType[] = [["Apples", 100, 2], ["Orange", true, City.London]]

result.forEach((elem: comboTupleType) => {
  console.log(`Tuple value: ${elem}`)
})

// Tuple value: Apples,100,2
// Tuple value: Orange,true,LON
```