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

### 另外一種斷言的語法
> 書上建議還是使用 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;
```