---
tags: Node.js 直播班 - 2023 春季班
---
# TypeScript 暖身讀書會 - 上半場
* [3/10(五) 20:00 線上 ZOOM 網址](https://us06web.zoom.us/j/84593105307)
## 環境建置
1. 安裝 VSCode 編輯器
2. [安裝 Node.js ](https://nodejs.org/zh-tw/download/)
3. 安裝 TypeScript `npm install -g typescript`
4. 新增專案資料夾後,打開終端機或命令提示字元視窗,進入您要建立專案的資料夾,然後輸入指定`tsc --init`
5. 新增 `hello.ts`,寫 `let sayHello: string = 'Hello World!';`
6. 執行 `tsc hello.ts` 來輸出
7. 也可用 `tsc --watch` 來進行監控
> 踩雷故事:不宣告 --init 時,會有重複宣告狀況
## 型別介紹
好處:編譯前就可以先幫你檢查
```=Typescript
// 字串型別
let name: string = 'Tom';
// 數值型別
let age: number = 18;
// 布林型別
let isStudent: boolean = true;
// 陣列型別
let hobbies: string[] = ['reading', 'coding', 'music'];
// 物件型別,後面會提到 interFace
const obj = {
num:3
}
obj.num = 3;
// 函式型別
function add(x: number, y: number): number {
return x + y;
}
// 任意型別
let data: any = 'Hello, world!';
data = 123;
data = true;
// 空值型別
function sayHello(): void {
console.log('Hello, world!');
}
// 永遠不型別
function throwError(): never {
throw new Error('Something went wrong!');
}
// 列舉型別
enum Gender {
Male,
Female,
Other
}
let gender: Gender = Gender.Male;
```
## 瀏覽器 DOM 與 Event 特殊型別
* HTMLElement:代表 `HTML` 元素節點。
* HTMLDivElement:代表 `<div>` 元素節點。
* HTMLSpanElement:代表 ``<span>`` 元素節點。
* HTMLAnchorElement:代表 `<a>` 元素節點。
* HTMLButtonElement:代表 `<button>` 元素節點。
* HTMLInputElement:代表 `<input>` 元素節點。
* HTMLTextAreaElement:代表 `<textarea>` 元素節點。
* HTMLSelectElement:代表 `<select>` 元素節點。
* HTMLTableElement:代表 `<table>` 元素節點。
* HTMLTableRowElement:代表 `<tr>` 元素節點。
* HTMLTableDataCellElement:代表 `<td>` 元素節點。
* MouseEvent:代表滑鼠事件。
* KeyboardEvent:代表鍵盤事件。
* TouchEvent:代表觸控事件。
* PointerEvent:代表指標事件。
* DragEvent:代表拖放事件。
* FocusEvent:代表焦點事件。
* InputEvent:代表輸入事件。
## interface 介紹
* [interface 指南導讀](https://willh.gitbook.io/typescript-tutorial/basics/type-of-object-interfaces)
TypeScript 中的 interface 是一種用來描述 JavaScript 中物件形狀的方式。它可以用來定義一個物件必須具備的屬性以及其類型,以及可以有哪些可選屬性和任意屬性等。
Person 範例:
```=typescript
interface Person {
firstName: string;
lastName: string;
age: number;
email?: string;
}
```
在上面的範例中,我們定義了一個名為 Person 的 interface,它包含了 firstName、lastName、age 這三個必須的屬性,分別是 string、string、number 類型的資料。另外,我們還定義了一個可選屬性 email,它的資料類型是 string。
使用 interface 可以讓程式碼更加明確和易於維護,讓開發者可以更清晰地知道物件的形狀和屬性的類型,有助於降低開發時出錯的風險。
:::spoiler 範例程式碼
```=typescript
interface User {
id: number;
name: string;
age: number;
email: string;
}
const users: User[] = [
{
id: 1,
name: 'Alice',
age: 25,
email: 'alice@example.com'
},
{
id: 2,
name: 'Bob',
age: 30,
email: 'bob@example.com'
},
{
id: 3,
name: 'Charlie',
age: 35,
email: 'charlie@example.com'
}
];
function addUser(newUser: User): void {
users.push(newUser);
}
function getUserById(userId: number): User | undefined {
return users.find(user => user.id === userId);
}
```
:::
## todolist 範例
:::spoiler index.html
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" class="txt" placeholder="請輸入待辦事項">
<input type="button" class="save" value="儲存待辦">
<ul class="list"></ul>
<script src="hello.js"></script>
</body>
</html>
```
:::
:::spoiler todo.ts
```=typescript
const txt = document.querySelector('.txt') as HTMLInputElement;
const save = document.querySelector('.save') as HTMLButtonElement;
const list = document.querySelector('.list') as HTMLUListElement;
interface ToDoItem {
content: string;
}
let data: ToDoItem[] = [];
function renderData(): void {
let str = '';
data.forEach(function (item, index) {
str += `<li>${item.content} <input class="delete" type="button" data-num="${index}" value="刪除待辦"></li>`;
});
list.innerHTML = str;
}
// 新增待辦功能
save.addEventListener('click', function(e: MouseEvent) {
if (txt.value == '') {
alert('請輸入內容');
return;
}
let obj: ToDoItem = { content: txt.value };
data.push(obj);
renderData();
});
// 刪除待辦功能
list.addEventListener('click', function(e: MouseEvent) {
if (!(e.target instanceof HTMLElement) || e.target.getAttribute('class') !== 'delete') {
return;
}
let num = parseInt(e.target.getAttribute('data-num')!);
console.log(num);
data.splice(num, 1);
alert('刪除成功!');
renderData();
});
```
:::
## ts.config 設定檔
以下是 `ts.config` 設定檔中 `compilerOptions` 裡面的設定項目,以表格形式進行詳細解釋。表格中包括了每個設定檔的屬性名稱、屬性介紹、預設值以及有效值。
* [Day 31. 戰線擴張・專案監控 X 編譯設定 - TypeScript Compiler Compile Configurations](https://ithelp.ithome.com.tw/articles/10222025)
* [TypeScript 編譯設定 - tsconfig.json](https://ithelp.ithome.com.tw/articles/10216636)
| 屬性名稱 | 屬性介紹 | 預設值 | 有效值 |
| :-- | --- | --- | --- |
| target | 編譯目標 | es2016 | ES3, ES5, ES6/ES2015, ES2016, ES2017, ES2018, ES2019, ES2020, ESNext |
| module | 生成模組規範 | CommonJS | ES2015, ES2020, AMD, System, UMD, CommonJS, None |
| lib | 編譯過程中需要包含的標準庫 | 無 | dom, dom.iterable, webworker, webworker.importscripts, es5, es6, es2015, es2016, es2017, es2018, es2019, es2020, esnext |
| allowJs | 允許編譯 JavaScript 文件 | false | true, false |
| checkJs | 是否檢查 JavaScript 文件的型別 | false | true, false |
| jsx | 設置 JSX 語法支援 | Preserve | Preserve, React, React-Native |
| declaration | 是否生成對應的 .d.ts 文件 | false | true, false |
| sourceMap | 是否生成對應的 .map 文件 | false | true, false |
| **outDir** | 編譯後的文件輸出目錄 | 無 | 相對路徑或絕對路徑 |
| **rootDir** | 編譯時需要被編譯的根目錄 | 無 | 相對路徑或絕對路徑 |
| removeComments | 是否移除註釋 | false | true, false |
| noEmit | 是否生成編譯後的文件 | false | true, false |
| strict | 是否開啟嚴格模式 | false | true, false |
| noImplicitAny | 是否禁止 any 型別 | false | true, false |
| strictNullChecks | 是否開啟嚴格的空值檢查 | false | true, false |
| noImplicitThis | 是否禁止 this 的隱式型別 | false | true, false |
| alwaysStrict | 是否在輸出文件中包含 "use strict" | false | true, false |
| noUnusedLocals | 是否檢查未使用的局部變數 | false | true, false |
| noUnused<br>Parameters | 是否檢查未使用的形參 | false | true, false |
| noImplicitReturns | 是否禁止缺少返回值的函數 | false | true, false |
| noFallthrough<br>CasesInSwitch | 是否禁止 switch 語句中的 fallthrough | false | true, false |
| baseUrl | 指定專案的根目錄,用來解析非相對模組名 | 無 | 相對路徑或絕對路徑 |
| paths | 設定模組的路徑映射 | 無 | 物件 |
| experimentalDecorators | 是否啟用實驗性的裝飾器語法 | false | true, false |
| emitDecorator<br>Metadata | 是否生成裝飾器元數據 | false | true, false |
| allowSynthetic<br>DefaultImports | 是否允許 import 默認為 export default 的模組 | false | true, false |
| noStrict<br>GenericChecks | 是否關閉泛型的嚴格型別檢查 | false | true, false |
| forceConsistent <br>CasingInFileNames | 強制文件名大小寫一致 | false | true, false |
| esModuleInterop | 是否允許 import 默認為 export 的模組 | false | true, false |
| preserveSymlinks | 是否保留符號鏈接 | false | true, false |
| skipLibCheck | 是否跳過標準庫檢查 | false | true, false |
透過這些設定,我們可以更好地控制 TypeScript 編譯器的行為,以符合不同的專案需求。如果有任何問題或需要進一步的解釋,請隨時提出。
## Vue、React、Angular 代辦事項差異
### Vue3
1. 安裝 Node.js
2. 執行環境 `npm create vite@latest todo-app -- --template vue-ts`
3. 在 `components` 資料夾載入 `TodoList.vue 元件`,範例碼如下
4. 在 `app.vue` 加入 `TodoList.vue 元件`
5. `npm run dev` 開啟伺服器
:::spoiler Todolist.vue
```=javascript
<template>
<div>
<h2>代辦清單</h2>
<div>
<input type="text" v-model="inputText" />
<button @click="addTodo">新增</button>
</div>
<ul>
<li v-for="(todo, index) in todos" :key="index">
{{ todo.text }}
<button @click="removeTodo(index)">刪除</button>
</li>
</ul>
</div>
</template>
<script lang="ts">
interface TodoItem {
text: string;
}
export default {
data() {
return {
inputText: '',
todos: [] as TodoItem[],
};
},
methods: {
addTodo() {
if (this.inputText.trim() === '') {
return;
}
this.todos.push({ text: this.inputText.trim() });
this.inputText = '';
},
removeTodo(index: number) {
this.todos.splice(index, 1);
},
},
};
</script>
```
:::
### React
1. 安裝 Node.js
2. 執行環境 `npm create vite@latest react-todo-app -- --template react-ts`
3. 在 `components` 資料夾載入 `TodoList.tsx 元件`,範例碼如下
4. 在 `App.tsx` 加入 `TodoList.tsx 元件`
5. `npm run dev` 開啟伺服器
:::spoiler TodoList.tsx
```javascript
import React, { useState } from 'react';
interface ToDoItem {
content: string;
}
const App: React.FC = () => {
const [inputValue, setInputValue] = useState<string>('');
const [toDoList, setToDoList] = useState<ToDoItem[]>([]);
const handleAddToDo = (): void => {
if (!inputValue.trim()) {
alert('請輸入內容');
return;
}
const newItem: ToDoItem = {
content: inputValue,
};
setToDoList([...toDoList, newItem]);
setInputValue('');
};
const handleDeleteToDo = (index: number): void => {
const newToDoList = [...toDoList];
newToDoList.splice(index, 1);
setToDoList(newToDoList);
};
return (
<div>
<h1>代辦清單</h1>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={handleAddToDo}>新增待辦</button>
<ul>
{toDoList.map((item, index) => (
<li key={index}>
{item.content}{' '}
<button onClick={() => handleDeleteToDo(index)}>刪除待辦</button>
</li>
))}
</ul>
</div>
);
};
export default App;
```
:::
### Angular
1. 安裝 angular CLI,`npm install -g @angular/cli`
2. 執行環境 `ng new my-app`
3. 調整 `app.componens.ts`、`app.modules.ts`,範例碼如下
4. 執行 `ng serve --open` 運作伺服器
:::spoiler app.componens.ts
``` =javascript
import { Component } from '@angular/core';
interface Todo {
content: string;
}
@Component({
selector: 'app-root',
template: `
<div>
<h1>Todo List</h1>
<form (submit)="addTodo()">
<input name="newTodo" type="text" [(ngModel)]="newTodo" placeholder="Add new todo" />
<button type="submit">Add</button>
</form>
<ul>
<li *ngFor="let todo of todos; let i = index">
{{ todo.content }}
<button (click)="removeTodo(i)">Remove</button>
</li>
</ul>
</div>
`,
styleUrls: ['./app.component.scss']
})
export class AppComponent {
newTodo: string = '';
todos: Todo[] = [];
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({ content: this.newTodo });
this.newTodo = '';
}
}
removeTodo(index: number) {
this.todos.splice(index, 1);
}
}
```
:::
:::spoiler app.modules.ts
```=javascript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
```
:::
### Node.js
1. 開新資料執行 `npm init` 後,安裝 `npm install --save-dev @types/uuid uuid ts-node`
2. 安裝 `ts-node` 套件 `npm install -g typescript
ts-node`
3. 執行 `tsc --init`
3. 新增一個 `server.ts` 檔案,程式碼如下
3. `ts-node server.ts`
:::spoiler server.ts 程式碼
```=typescript
import http from 'http';
import { v4 as uuidv4 } from 'uuid';
interface Todo {
title: string;
id: string;
}
const todos: Todo[] = [];
const requestListener = (req: http.IncomingMessage, res: http.ServerResponse) => {
const headers = {
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Content-Length, X-Requested-With',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PATCH, POST, GET,OPTIONS,DELETE',
'Content-Type': 'application/json'
};
let body = '';
req.on('data', chunk => {
body += chunk;
});
if (req.url === '/todos' && req.method === 'GET') {
res.writeHead(200, headers);
res.write(JSON.stringify({
'status': 'success',
'data': todos,
}));
res.end();
} else if (req.url === '/todos' && req.method === 'POST') {
req.on('end', () => {
try {
const title = JSON.parse(body).title;
if (title !== undefined) {
const todo: Todo = {
'title': title,
'id': uuidv4(),
};
todos.push(todo);
res.writeHead(200, headers);
res.write(JSON.stringify({
'status': 'success',
'data': todos,
}));
res.end();
} else {
res.writeHead(400, headers);
res.write(JSON.stringify({
'status': 'false',
'message': '欄位未填寫正確,或無此 todo id',
}));
res.end();
}
} catch (error) {
res.writeHead(400, headers);
res.write(JSON.stringify({
'status': 'false',
'message': '欄位未填寫正確,或無此 todo id',
}));
res.end();
}
});
} else if (req.method === 'OPTIONS') {
res.writeHead(200, headers);
res.end();
} else {
res.writeHead(404, headers);
res.write(JSON.stringify({
'status': 'false',
'message': '無此網站路由',
}));
res.end();
}
};
const server = http.createServer(requestListener);
server.listen(3005);
```
:::
## 導入結論
1. 先導入型別與 `interface` 試試手感
2. 用 chatGPT 協助轉檔,等熟悉後再來手寫
3. 要注意 chatGPT 他的模型只有學到 2021
## 洧杰學習資源
1. [Discord 同學的分享](https://discord.com/channels/801807326054055996/1077558592401588234)
2. [TypeScript 新手指南](https://willh.gitbook.io/typescript-tutorial/)
3. ChatGPT [對話一](https://sharegpt.com/c/R1BdXYm)
4. [保哥](https://www.youtube.com/watch?v=SCLKlc6G0_k)與[其他講者](https://www.youtube.com/watch?v=EEdd8zov4-w)的劍與盾
5. ChatGPT [對話二](https://sharegpt.com/c/KYfEt9I)
:::spoiler 團隊間先不要用太複雜的 typescript
![](https://i.imgur.com/LffIEuG.png)
![](https://i.imgur.com/dgnOm4k.png)
:::