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