---
# System prepended metadata

title: TypeScript 基礎應用入門，並與 Next.js 進行整合開發

---

# TypeScript 基礎應用入門，並與 Next.js 進行整合開發


## 第 1 章：TypeScript 簡介與環境設置

**TypeScript = JS 超集 + 靜態型別系統**。藉由編譯期偵錯、完善的 IDE 體驗（自動完成、跳轉、重構），提升大型專案的可靠性與維護性。

### 推薦安裝方式（本地開發依賴）

> 不建議全域安裝 `tsc` 當作日常開發主流程，改用專案本地依賴更易控版本。

```bash
# 初始化專案（空資料夾）
npm init -y

# 安裝 TS 與型別（開發依賴）
npm i -D typescript @types/node

# 產生 tsconfig.json
npx tsc --init
```

### tsconfig 建議（現代前端）

```jsonc
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "Node",
    "jsx": "react-jsx",
    "strict": true,
    "noFallthroughCasesInSwitch": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": ["src"]
}
```

> 若需 IE11（極少數舊專案）才考慮 `target: "ES5"`；現代 React 不再支援 IE11。

### Hello, TypeScript

```ts
// src/hello.ts
console.log("Hello, TypeScript!")
```

```bash
npx tsc src/hello.ts && node src/hello.js
```

參考：`feature/typescript`、`feature/typescript-hello`（你的範例倉庫）。

---

## 第 2 章：基本型別（精要）

* **number / string / boolean**：`let n: number = 42`、模板字面量型別可組裝字串。
* **null / undefined**：常見於可空欄位，與 `strictNullChecks` 搭配更安全。
* **any vs unknown**：`unknown` 更安全（需先縮小/斷言才能使用）。
* **void / never**：`void` 表示不回傳；`never` 表示永不返回（throw 或無限迴圈）。
* **object**：非原始型別；在專案中更常使用自定義物件型別。
* **symbol**：唯一鍵；偶見於進階框架開發。
* **型別推斷**：TS 會從初始值推斷型別，善用推斷可減少噪音。

---

## 第 3 章：變數、常量與作用域

```ts
let age: number = 25
const name = "John" // const 推斷為字面量型別，可更精準
// name = "Doe" // ❌ 不能再賦值
```

> 與 `var` 相比，`let/const` 具塊級作用域，避免提升與重複宣告問題。

---

## 第 4 章：函式與箭頭函式

```ts
function greet(name: string = "Anonymous"): string {
  return `Hello, ${name}!`
}

// 箭頭函式 + 明確回傳型別
const add = (a: number, b: number): number => a + b

// 可選參數
function log(msg: string, level?: "info" | "warn" | "error") {
  console[level ?? "info"]?.(msg)
}

// 函式多載（overload）
function toArray(x: string): string[]
function toArray(x: number): number[]
function toArray(x: string | number) { return [x] }
```

---

## 第 5 章：物件、陣列、元組

```ts
// 物件
const person: { name: string; age?: number; readonly id: string } = {
  id: "u_1", name: "Alice"
}

// 陣列
const nums: number[] = [1,2,3]
const words: Array<string> = ["a","b"] // 等價寫法

// 只讀陣列
const readonlyNums: readonly number[] = [1,2,3]
// readonlyNums.push(4) // ❌

// 元組
const userEntry: [id: string, score: number] = ["u_1", 98]

// 聯合型別陣列
const mixed: (number | string)[] = [1, "two", 3]
```

---

## 第 6 章：`type` 與 `interface`

**相同點**：都可描述物件形狀、支援可選屬性與函式型別。

**差異與建議**：

* `interface` 支援多次宣告合併、`extends`（在 SDK/公共 API 常用）。
* `type` 支援 **聯合 / 交叉 / 條件 / 映射** 型別，組合力更強。
* 在 React 專案中：

  * **Props/State** 可用 `type` 或 `interface` 皆可；偏好多用 `type` 來搭配聯合與工具型別。

```ts
type User = { id: string; name: string }
interface Node { id: string }
interface Person extends Node { name: string }

type Employee = User & { role: "dev" | "pm" }
```

---

## 第 7 章：Class（瞭解即可）

React 函式元件為主流，Class 在前端較少見；但在工具程式或 OOP 情境仍實用。

```ts
class Dog {
  breed = "Mixed"
  name = "Unknown"
  bark() { console.log("Woof!") }
}
const d = new Dog(); d.name = "Buddy"; d.bark()
```

---

## 第 8 章：Enum 與替代方案

```ts
enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT" }
```

> 前端專案更常用 **字串聯合型別** 取代 `enum`，因為 tree-shaking 友善、可讀值直接就是字串：

```ts
type Direction2 = "UP" | "DOWN" | "LEFT" | "RIGHT"
```

> `const enum` 會 inline 值、體積小，但與 `isolatedModules`/Babel 流程相處不佳，慎用。

---

## 第 9 章：泛型（Generics）

```ts
function identity<T>(x: T): T { return x }
const a = identity<string>("hi")
const b = identity(42) // 推斷為 number

class Box<T> { constructor(public value: T) {} }
const s = new Box<string>("txt")

interface Pair<K, V> { key: K; value: V }
const p: Pair<string, number> = { key: "age", value: 30 }

// 受限泛型
interface HasLength { length: number }
function logLength<T extends HasLength>(item: T) { console.log(item.length) }
```

---

# 第二部分：在 React 中使用 TypeScript

## 建立 React + TS 專案

**Vite（推薦）**

```bash
npm create vite@latest my-app -- --template react-ts
cd my-app && npm i && npm run dev
```

**Create React App（舊方案）**

```bash
npx create-react-app my-app --template typescript
```

## tsconfig（React 專案範例）

```jsonc
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "Node",
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] }
  },
  "include": ["src"]
}
```

**Vite 路徑別名**（`vite.config.ts`）

```ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
  plugins: [react()],
  resolve: { alias: { '@': path.resolve(__dirname, 'src') } }
})
```

## 元件與 Props 型別

```tsx
// 方式一：具名 Props 型別 + 傳統函式
type GreetingProps = { name: string; children?: React.ReactNode }
function Greeting({ name, children }: GreetingProps) {
  return <h1>Hello, {name}! {children}</h1>
}

// 方式二：React.FC（注意 children 已內建，但不易自訂 defaultProps）
interface ButtonProps { onClick?: () => void; disabled?: boolean }
const Button: React.FC<ButtonProps> = ({ children, ...rest }) => (
  <button {...rest}>{children}</button>
)
```

> 實務上 **不一定要用 `React.FC`**；直接為函式標註 props 型別即可，較直觀。

## 事件與 DOM 型別

```tsx
function SearchBox() {
  const [q, setQ] = React.useState("")
  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => setQ(e.target.value)
  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault() }
  return (
    <form onSubmit={onSubmit}>
      <input value={q} onChange={onChange} />
      <button>Go</button>
    </form>
  )
}
```

## 狀態與可空型別

```tsx
const [user, setUser] = React.useState<null | { id: string; name: string }>(null)
// 之後以 type narrowing 使用 user?.name 或條件判斷
```

## Context 與自訂 Hook 型別

```tsx
// Context
type Auth = { user: { id: string; name: string } | null; login: () => void }
const AuthCtx = React.createContext<Auth | undefined>(undefined)
function useAuth() {
  const ctx = React.useContext(AuthCtx)
  if (!ctx) throw new Error('useAuth must be used within <AuthProvider>')
  return ctx
}
```

## CSS Modules 型別宣告

```ts
// src/global.d.ts
declare module '*.module.css' { const classes: Record<string, string>; export default classes }
declare module '*.module.scss' { const classes: Record<string, string>; export default classes }
```

## 常見第三方型別

```bash
npm i -D @types/react-router-dom @types/lodash
```

> 很多套件已內建型別，否則在 DefinitelyTyped 以 `@types/` 取得。

---

# 第三部分：在現有 React 專案中引入 TypeScript（增量遷移）

## 步驟總覽

1. 安裝依賴：

```bash
npm i -D typescript @types/node @types/react @types/react-dom
```

2. 新增 `tsconfig.json`（見上方建議）。
3. **逐檔改名**：`.js`→`.ts`、`.jsx`→`.tsx`。
4. **允許混用**：暫時保留 `.js`（`allowJs: true`）。也可用 `// @ts-check` + JSDoc 在 JS 中先享型別檢查。
5. 逐步加入型別註解（props、state、事件、API 回傳）。
6. `npx tsc --noEmit` 持續檢查，修正錯誤。

參考：`feature/app`、`feature/react-typescript-init`（你的範例倉庫）。

## 例：JS → TS 改寫

```tsx
// App.tsx
import { useState } from 'react'

export default function App() {
  const [count, setCount] = useState<number>(0)
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}
```

## API 回傳型別與資料安全

```ts
type Todo = { id: number; title: string; completed: boolean }
async function fetchTodos(): Promise<Todo[]> {
  const r = await fetch('/api/todos')
  const data = await r.json() as Todo[] // 更安全：配合 zod/io-ts 驗證
  return data
}
```

---

# 第四部分：工具化（ESLint / Prettier / tsconfig 嚴格度）

## ESLint + typescript-eslint（Vite 預設可擴充）

```bash
npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
```

```js
// .eslintrc.cjs
module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'react', 'react-hooks'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended'
  ],
  settings: { react: { version: 'detect' } }
}
```

## Prettier（格式化）

```bash
npm i -D prettier eslint-config-prettier eslint-plugin-prettier
```

```js
// .prettierrc.json
{ "singleQuote": true, "semi": false, "trailingComma": "es5" }
```

## 嚴格度微調（tsconfig）

* `strict: true`（建議）
* 常用：`noImplicitAny`, `strictNullChecks`, `noUncheckedIndexedAccess`（更嚴謹）

---

# 第五部分：常見踩坑與最佳實務

* **`any` 氾濫** → 以 `unknown` / 型別守衛 / 泛型替代。
* **`React.FC` 一律使用？** → 不必。直接為函式標 props 型別更靈活。
* **`defaultProps` in FC** → 已不推薦；用參數預設值或 `Partial<T>`。
* **可空狀態** → 初始 `null` 會變 `T | null`，渲染前加守衛或 `?.`。
* **事件型別** → `React.ChangeEvent<HTMLInputElement>`、`React.MouseEvent<HTMLButtonElement>`。
* **ref / forwardRef** → `const Comp = React.forwardRef<HTMLDivElement, Props>((props, ref) => ...)`。
* **CSS Modules 型別** → 記得加 `global.d.ts` 宣告。
* **路徑別名** → tsconfig + bundler（Vite/webpack）需一致。
* **Enum vs 字串聯合** → 前端多用字串聯合；必要時才上 enum。
* **工具型別** → `Partial`/`Pick`/`Omit`/`Record`/`ReturnType`/`Parameters`/`NonNullable` 善用。
* **`satisfies`**（TS 4.9+） → 保留推斷又驗證結構：

```ts
const routes = {
  home: { path: '/' },
  about: { path: '/about' }
} as const satisfies Record<string, { path: string }>
```

---

# 第六部分：小型實作練習清單

1. **型別化 TodoList**：`Todo` 型別、API 型別、`useReducer` 管理狀態。
2. **表單元件庫**：輸入元件 `value/onChange` 泛型化，事件型別正確。
3. **資料抓取 hooks**：`useFetch<T>()` 泛型回傳，搭配 `AbortController`。
4. **路由型別**：`useParams<{ id: string }>()`（React Router v6）。
5. **Context + Reducer**：以型別守衛確保 `dispatch` 的 action payload 正確。

---

## 參考與你的範例倉庫

* 初始化專案：`feature/typescript`、`feature/typescript-hello`
* React TS 起步與遷移：`feature/app`、`feature/react-typescript-init`

> 完成本教材後，你應能：讀寫 TS 型別、在 React 中自信地型別化所有常見模式、把既有 JS 專案 **增量遷移** 至 TypeScript，並建立一套可靠的團隊規範。
