---
# System prepended metadata

title: TypeScript 暖身讀書會 - 下半場
tags: [Node.js 直播班 - 2023 春季班]

---

---
tags: Node.js 直播班 - 2023 春季班
---

# TypeScript 暖身讀書會 - 下半場

* [3/17(五) 20:00 線上 ZOOM 網址](https://us06web.zoom.us/j/84593105307)


## 大綱
1. 補充 TypeScript 常見語法
2. 以此遊戲來延伸語法 [beginners-typescript-tutorial](https://github.com/total-typescript/beginners-typescript-tutorial)，感謝 alphatero 推薦
3. [TypeScript 新手指南](https://willh.gitbook.io/typescript-tutorial/)、[Discord 同學討論](https://discord.com/channels/801807326054055996/1077558592401588234)

## interface 補充 - 可選屬性

* [React](https://github.com/lujakob/nestjs-realworld-example-app) - 放在各元件裡
* [angular](https://github.com/gothinkster/angular-realworld-example-app/tree/9e8c49514ee874e5e0bbfe53ffdba7d2fd0af36f/src/app/core/models) - 獨立 models 資料夾
* [React - Model](https://github.com/piotrwitek/react-redux-typescript-guide/blob/e0532ae099a1ddcce7cf263280e20714c7a53e72/playground/src/models/user.ts) - 放在 Model 裡面
* [Node](https://github.com/microsoft/TypeScript-Node-Starter/blob/master/src/models/User.ts) - 放在 model 裡
* [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template/tree/88b7440b3330324cf639d5cbeb5350994d020473/src/store/modules) - 放在各 modules
* [vben-admin-thin-next](https://github.com/vbenjs/vben-admin-thin-next/tree/07c2567751c711541a4ff24af00f2cd018c18eab/src/api/sys/model) - models、[取出 user 表](https://github.com/vbenjs/vben-admin-thin-next/blob/07c2567751c711541a4ff24af00f2cd018c18eab/src/api/sys/user.ts)

```
// 範例一：出錯版本
interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom'
};
// 範例二：可選屬性
interface Person {
    name: string;
    age?: number;
}

let tom: Person = {
    name: 'Tom'
};
```


## 聯合型別

允許變數可以同時擁有不同的類型。可以更靈活地定義變數的類型，讓程式碼更容易閱讀和維護。

```
// 定義一個可能是數字或字串的變數
let myVar: number | string;
myVar = "hello"; // 正確
myVar = 123; // 正確
myVar = true; // 錯誤

// 印出 ID
function printID(id: number | string): void {
  console.log("ID is: ", id);
}

printID(123); // ID is: 123
printID("abc"); // ID is: abc

// 錯誤範例
function displayData(data: string | number): void {
    console.log(data.toFixed(2));
}

displayData("hello"); // HELLO
displayData(3.14159); // 3.14
```


## 型別推論

能夠根據變數的初始值自動推斷出該變數的型別，讓開發者在寫程式時更容易避免錯誤。



正確範例：
```
let weight = 60 // 體重（公斤）
let height = 170 // 身高（公分）
let bmi = weight / ((height/100) ** 2) // BMI 值
console.log(`BMI 值為 ${bmi}`)

```

問題：

```
let numbers = [5, 2, 8, 1, 9] // 一個數字陣列
numbers.push("33");
console.log(`排序後的陣列：${numbers}`)
```

```
let num = "123";
num = 456;
```

```
function add(a, b) {
  return a + b;
}

add(1, "2");
```

## 泛型

泛型技術可將型別作為參數傳遞，提高程式碼可重用性及強型別語言的靈活性。應用於函式、類別、介面等元素，讓不同型別使用相同的代碼，減少重複代碼。

### 兩個函式各自處理
```=typescript
function reverseNumbers(data: number[]): number[] {
  return data.reverse();
}

function reverseString(data: string): string {
  return data.split("").reverse().join("");
}

// 反轉一個數組
const numbers = [1, 2, 3, 4, 5];
console.log(reverseNumbers(numbers)); // [5, 4, 3, 2, 1]

// 反轉一個字串
const str = "hello";
console.log(reverseString(str)); // 'olleh'
```

改用泛型寫法後

```=typescript
function reverse<T>(data: T[]): T[] {
return data.reverse();
}


// 反轉一個數組
const numbers = [1, 2, 3, 4, 5];
console.log(reverse<number>(numbers)); // [5, 4, 3, 2, 1]

// 反轉一個字串
const str = "hello";
console.log(reverse<string>(str.split(""))); // ['o', 'l', 'l', 'e', 'h']
```


**範型約束 (generic constraint)**，可限制他代入的參數型別
```
function reverse<T extends string | number>(data: T[]): T[] {
  return data.reverse();
}

```

**使用泛型定義兩個參數**，分別為 T 和 U，並返回一個 Tuple 類型的陣列，其中第一個元素為 T 型別的值，第二個元素為 U 型別的值：

```=typescript

function makeTuple<T, U>(value1: T, value2: U): [T, U] {
  return [value1, value2];
}

const tuple = makeTuple("hello", 123);
// tuple 的型別為 [string, number]，值為 ["hello", 123]
```

限制 T 與 U 只支援數字和字串型別：
```=typescript
function concat<T extends string | number, U extends string | number>(a: T, b: U): string {
  return `${a}${b}`;
}

console.log(concat("hello", "world")); // helloworld
console.log(concat(1, 2)); // 12
```


**async 需定義回傳的內容是 Promise 格式**，而這個 Promise 的泛型類型就是由最右邊的 <Post> 定義的。

在定義 async 函式時，並不需要在括號中定義泛型類型，因為 TypeScript 會根據函式中的 await 表達式自動推斷返回的 Promise 的泛型類型。而最右邊的 <Post> 是為了明確告訴 TypeScript 該 Promise 的泛型類型，可以提高程式碼可讀性。

```=typescript
interface Post {
  id: number;
  title: string;
  body: string;
}

export const getPostItem = async (): Promise<Post> => {
  const data = await fetch("https://swapi.dev/api/people/1").then((res) => {
    return res.json();
  });

  return data;
};

```
    
