## 陣列應用
### 有指定型別的陣列
```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]));
```

#### 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));
})
```

##### 潛在問題一(應該預防空陣列)
```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));
})
```

還是可以如期印出數值,不會出錯

##### 潛在問題二(never型別)
````ts=
// tsconfig.json
{
"compilerOptions": {
"target": "es2020",
"rootDir": "./src",
"outDir": "./dist",
"declaration": true,
"strictNullChecks": true //嚴格禁止null與undefined
//也禁止推論為any型別
}
}
````

無法填入任何東西到這個陣列之中

如果在這情況下也可以操作成功 -> **幫空陣列加上斷言**
```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]));
````

##### 處理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: 使用一個名稱還存取一系列有特定名稱的嘗數值

````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}`);
})
```

在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
```