# TypeScript 7 靜態型別 ## 7-2-1 JS 動態型別 > 擁有型別的是值本身,而不是變數 ```typescript! let myVar; console.log(`${myVar} = ${typeof myVar}`) myVar = 12; console.log(`${myVar} = ${typeof myVar}`) myVar = 'Hello'; console.log(`${myVar} = ${typeof myVar}`) myVar = true; console.log(`${myVar} = ${typeof myVar}`) ``` ``` undefined = undefined 12 = number Hello = string true = boolean ``` ## 7-2-2 型別註記 > amount 爲 number > calculateTax return 值爲 number ```typescript= function calculateTax(amount: number): number { return amount * 1.2; } console.log(`${12} = ${calculateTax(12)}`) console.log(`${"Hello"} = ${calculateTax("Hello")}`) console.log(`${true} = ${calculateTax(true)}`) ``` ```! src/index.ts(6,42): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. src/index.ts(7,39): error TS2345: Argument of type 'boolean' is not assignable to parameter of type 'number'. 3:22:51 PM - Found 2 errors. Watching for file changes. ``` --- > 型別註記也可以用在變數上 ```typescript! let price: number = 100; ``` ## 7-2-3 隱性推論的靜態型別 > TypeScript 能夠根據變數宣告時被指派的值推論型別 ```type= function calculateTax(amount: number): number { return amount * 1.2; } let price = 100; let taxAmount = calculateTax(price); let halfShare = taxAmount / 2; console.log(`Full amount in tax: ${taxAmount}`); console.log(`Half share: ${halfShare}`); ``` --- ```typescript= function calculateTax(amount: number) { return (amount * 1.2).toFixed(2); } let price = 100; let taxAmount = calculateTax(price); let halfShare = taxAmount / 2; console.log(`Full amount in tax: ${taxAmount}`); console.log(`Half share: ${halfShare}`); ``` ```! src/index.ts(7,17): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. ``` ```json { "compilerOptions": { "target": "es2020", "outDir": "./dist", "rootDir": "./src", "declaration": true # << } } ``` 重新編譯後,在 ./dist/index.d.ts ```typescript= declare function calculateTax(amount: number): string; declare let price: number; declare let taxAmount: string; declare let halfShare: number; ``` ## 7-2-4 使用 any 型別 > 使用 any 表示接受任何型別,所以需要自行承擔誤用型別的責任 ```typescript= function calculateTax(amount: any): any { return `$${(amount * 1.2).toFixed(2)}`; } let price = 100; let taxAmount = calculateTax(price); let halfShare = taxAmount / 2; console.log(`Price: ${price}`); console.log(`Full amount in tax: ${taxAmount}`); console.log(`Half share: ${halfShare}`); ``` ```! Price: 100 Full amount in tax: $120.00 Half share: NaN ``` ## 7-2-5 隱性推論的 any 型別 ```typescript= function calculateTax(amount): any { return `$${(amount * 1.2).toFixed(2)}`; } let price = 100; let taxAmount = calculateTax(price); let halfShare = taxAmount / 2; let personaVal = calculateTax("Bob"); console.log(`Price: ${price}`); console.log(`Full amount in tax: ${taxAmount}`); console.log(`Half share: ${halfShare}`); console.log(`Name: ${personaVal}`); ``` ```! Price: 100 Full amount in tax: $120.00 Half share: NaN Name: $NaN ``` ```typescript= declare function calculateTax(amount: any): any; ``` ## 7-2-6 禁止隱性推論 any 型別 ```json= { "compilerOptions": { "target": "es2020", "outDir": "./dist", "rootDir": "./src", "declaration": true, "noImplicitAny": true # << } } ``` ```! src/index.ts(1,23): error TS7006: Parameter 'amount' implicitly has an 'any' type. ``` ## 7-3-1 型別聯集 ```typescript= function calculateTax(amount: number, format: boolean): string | number { const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxNumber: string | number = calculateTax(100, false); let taxString: string | number = calculateTax(100, true); console.log(`Number value: ${taxNumber.toFixed(2)}`); console.log(`String value: ${taxString.charAt(0)}`); ``` > 對於型別聯集來說,你只能使用所有型別都有定義的屬性與方法 > number 與 string 共有的就只有 toString() 方法而已 ```! src/index.ts(9,40): error TS2339: Property 'toFixed' does not exist on type 'string | number'. Property 'toFixed' does not exist on type 'string'. src/index.ts(10,40): error TS2339: Property 'charAt' does not exist on type 'string | number'. Property 'charAt' does not exist on type 'number'. ``` ## 7-3-2 型別斷言 > 型別聯集的問題之一是值原有的許多方法會無法使用 > 可以使用型別斷言將值限縮爲其中一種型別 > 注意:型別斷言不是"型別轉換" ```typescript= function calculateTax(amount: number, format: boolean): string | number { const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxNumber: string | number = calculateTax(100, false) as number; let taxString: string | number = calculateTax(100, true) as string; console.log(`Number value: ${taxNumber.toFixed(2)}`); console.log(`String value: ${taxString.charAt(0)}`); ``` ```! Number value: 120.00 String value: $ ``` ## 7-3-3 在斷言時使用錯誤的型別 ```typescript= function calculateTax(amount: number, format: boolean): string | number { const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxBoolean = calculateTax(100, true) as boolean console.log(`Boolean value: ${taxBoolean}`) ``` ```! src/index.ts(6,18): error TS2352: Conversion of type 'string | number' to type 'boolean' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type 'number' is not comparable to type 'boolean'. ``` 如果想讓 taxBoolean 可以這樣寫 ```typescript= function calculateTax(amount: number, format: boolean): string | number { const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxBoolean = calculateTax(100, true) as any as boolean; console.log(`Boolean value: ${taxBoolean}`) ``` ```! Boolean value: $120.00 ``` ![](https://hackmd.io/_uploads/r16htbSWp.png) ### 另外一種斷言的語法 > 書上建議還是使用 as xxx 的語法 ```typescript= let taxString = calculateTax(100, true) as string; let taxString = <string> calculateTax(100, true); ``` ## 7-3-4 型別防衛敘述 ```typescript= function calculateTax(amount: number, format: boolean): string | number { const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxValue = calculateTax(100, false); if (typeof taxValue === 'number') { console.log(`Number Value: ${taxValue.toFixed(2)}`); } else if (typeof taxValue === 'string') { console.log(`String Value: ${taxValue.charAt(0)}`); } ``` ## 7-4-1 使用 unknown 型別 > 比 any 更安全的替代方案 ```typescript= function calculateTax(amount: number, format: boolean): string | number { const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxValue = calculateTax(100, false); switch (typeof taxValue) { case "number": console.log(`Number Value: ${taxValue.toFixed(2)}`); break; case "string": console.log(`String Value: ${taxValue.charAt(0)}`); break; default: let value: never = taxValue; console.log(`Unexpected type for value: ${value}`); } let newResult: unknown = calculateTax(200, false); let myNumber: number = newResult; console.log(`Number Value: ${myNumber.toFixed(2)}`); ``` ```! src/index.ts(21,5): error TS2322: Type 'unknown' is not assignable to type 'number'. ``` > 需使用型別斷言將 unknown 的值指派給其他型別,所以更爲安全 ```diff= let newResult: unknown = calculateTax(200, false); - let myNumber: number = newResult; + let myNumber: number = newResult as number; ``` ## 7-4-2 使用可爲 null 的型別 > 因爲 TypeScript 預設允許 null 與 undefined 被指派給所有型別的值,所以指派 null 給 number 不會出現編譯錯誤 ```typescript= function calculateTax(amount: number, format: boolean): string | number { if (amount === 0) { return null; } const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxValue: string | number = calculateTax(0, false); switch (typeof taxValue) { case "number": console.log(`Number Value: ${taxValue.toFixed(2)}`); break; case "string": console.log(`String Value: ${taxValue.charAt(0)}`); break; default: if (taxValue === null) { console.log("Value is null"); } else { let value: never = taxValue; console.log(`Unexpected type for value: ${value}`); } } let newResult: unknown = calculateTax(200, false); let myNumber: number = newResult as number; console.log(`Number value: ${myNumber.toFixed(2)}`); ``` ```! Value is null Number value: 240.00 ``` ## 7-4-3 限制 null 的使用 ```json= { "compilerOptions": { "target": "es2020", "outDir": "./dist", "rootDir": "./src", "declaration": true, "noImplicitAny": true, "strictNullChecks": true # << } } ``` ```! src/index.ts(3,5): error TS2322: Type 'null' is not assignable to type 'string | number'. ``` 可執行的程式 ```typescript= function calculateTax(amount: number, format: boolean): string | number | null { if (amount === 0) { return null; } const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxValue: string | number | null = calculateTax(0, false); switch (typeof taxValue) { case "number": console.log(`Number Value: ${taxValue.toFixed(2)}`); break; case "string": console.log(`String Value: ${taxValue.charAt(0)}`); break; default: // typeof null === "object" if (taxValue === null) { console.log("Value is null"); } else { let value: never = taxValue; console.log(`Unexpected type for value: ${value}`); } } let newResult: unknown = calculateTax(200, false); let myNumber: number = newResult as number; console.log(`Number value: ${myNumber.toFixed(2)}`); ``` ## 7-4-4 用斷言從型別聯集中移除 null > 非 null 值斷言的語法是在值的後面加上 `!` > 只有在你明確知道 null 值絕對不會發生時才可以使用 ```typescript= let taxValue: string | number = calculateTax(0, false)!; ``` ## 7-4-5 用型別防衛敘述從聯集中排除 null ```typescript= function calculateTax(amount: number, format: boolean): string | number | null { if (amount === 0) { return null; } const calcAmount = amount * 1.2; return format ? `$${calcAmount.toFixed(2)}` : calcAmount; } let taxValue: string | number | null = calculateTax(100, false)! if (taxValue !== null) { let nonNullTaxValue: string | number = taxValue; switch (typeof nonNullTaxValue) { case "number": console.log(`Number Value: ${nonNullTaxValue.toFixed(2)}`); break; case "string": console.log(`String Value: ${nonNullTaxValue.charAt(0)}`); break; } } else { console.log("Value is not a string or a number"); } ``` ## 7-4-6 使用明確賦值斷言 ```typescript= let taxValue: string | number | null; eval("taxValue = 100") console.log(`Tax value: ${taxValue}`); ``` ```! src/index.ts(3,27): error TS2454: Variable 'taxValue' is used before being assigned. ``` ```diff= - let taxValue: string | number | null; + let taxValue!: string | number | null; ```