TypeScript 函式類型與重載
函式是程式的基本構建塊。本篇將帶你掌握 TypeScript 中函式類型的所有面向。
一、 基本函式類型
1.1 參數與回傳值
typescript
// 具名函式
function add(a: number, b: number): number {
return a + b
}
// 箭頭函式
const multiply = (a: number, b: number): number => {
return a * b
}
// 簡寫(單一表達式)
const divide = (a: number, b: number): number => a / b1.2 類型推論
typescript
// 回傳值可以推論
function add(a: number, b: number) {
return a + b // 推論回傳 number
}
// 參數必須標註(無法推論)
// function add(a, b) {} // 錯誤!1.3 void 回傳
typescript
// 沒有回傳值
function log(message: string): void {
console.log(message)
}
// 箭頭函式
const greet = (name: string): void => {
console.log(`Hello, ${name}!`)
}二、 可選參數與預設值
2.1 可選參數 (?)
typescript
function greet(name: string, greeting?: string): string {
return `${greeting ?? "Hello"}, ${name}!`
}
greet('John') // 'Hello, John!'
greet('John', 'Hi') // 'Hi, John!'WARNING
可選參數必須放在必填參數之後!
typescript
// 正確
function fn(a: string, b?: number): void {}
// 錯誤
// function fn(a?: string, b: number): void {}2.2 預設參數值
typescript
function greet(name: string, greeting: string = 'Hello'): string {
return `${greeting}, ${name}!`
}
greet('John') // 'Hello, John!'
greet('John', 'Hi') // 'Hi, John!'
// 預設值參數可以不在最後
function createUser(name: string, age: number = 18, email: string) {
return { name, age, email }
}
createUser('John', undefined, 'john@example.com') // age = 18三、 剩餘參數(Rest Parameters)
3.1 收集所有參數
typescript
function sum(...numbers: number[]): number {
return numbers.reduce((acc, num) => acc + num, 0)
}
sum(1, 2, 3) // 6
sum(1, 2, 3, 4, 5) // 153.2 混合使用
typescript
function log(prefix: string, ...messages: string[]): void {
messages.forEach((msg) => console.log(`${prefix}: ${msg}`));
}
log('INFO', 'Starting app', 'Loading config')
// INFO: Starting app
// INFO: Loading config3.3 元組型態的剩餘參數
typescript
function formatDate(...args: [number, number, number]): string {
const [year, month, day] = args
return `${year}-${month}-${day}`
}
formatDate(2025, 1, 13) // '2025-1-13'四、 函式型態表達式
4.1 定義函式型態
typescript
// 函式型態表達式
type MathOperation = (a: number, b: number) => number
const add: MathOperation = (a, b) => a + b
const subtract: MathOperation = (a, b) => a - b
const multiply: MathOperation = (a, b) => a * b4.2 作為參數
typescript
type Callback = (result: number) => void
function calculate(
a: number,
b: number,
operation: (x: number, y: number) => number,
callback: Callback
): void {
const result = operation(a, b)
callback(result)
}
calculate(
10,
5,
(a, b) => a + b,
(result) => {
console.log(`Result: ${result}`) // Result: 15
}
)4.3 作為回傳值
typescript
function createAdder(base: number): (n: number) => number {
return (n: number) => base + n
}
const add10 = createAdder(10)
console.log(add10(5)) // 15
console.log(add10(20)) // 30五、 函式重載(Overloads)
5.1 為什麼需要重載?
typescript
// 這樣寫無法精確表達
function process(input: string | number): string | number {
if (typeof input === 'string') {
return input.toUpperCase()
}
return input * 2
}
const result = process('hello') // string | number,不精確!5.2 使用重載
typescript
// 重載簽名
function process(input: string): string
function process(input: number): number
// 實作簽名
function process(input: string | number): string | number {
if (typeof input === 'string') {
return input.toUpperCase()
}
return input * 2
}
const str = process('hello') // string
const num = process(10) // number5.3 多種重載
typescript
// 根據參數數量重載
function createElement(tag: string): HTMLElement
function createElement(tag: string, content: string): HTMLElement
function createElement(
tag: string,
attrs: object,
content: string
): HTMLElement
function createElement(
tag: string,
attrsOrContent?: string | object,
content?: string
): HTMLElement {
const el = document.createElement(tag)
if (typeof attrsOrContent === 'string') {
el.textContent = attrsOrContent
} else if (attrsOrContent) {
Object.assign(el, attrsOrContent)
if (content) el.textContent = content
}
return el
}
createElement('div')
createElement('p', 'Hello')
createElement('a', { href: '#' }, 'Link')5.4 重載注意事項
typescript
// 1. 重載簽名必須在實作簽名之前
// 2. 實作簽名對外不可見
// 3. 實作簽名必須兼容所有重載簽名
// 不能這樣呼叫(實作簽名不可見)
// process('hello', 123) // 錯誤六、 this 類型
6.1 明確 this 類型
typescript
interface User {
name: string
greet(this: User): void
}
const user: User = {
name: 'John',
greet() {
console.log(`Hello, ${this.name}!`)
},
}
user.greet() // OK
const greet = user.greet
// greet() // 錯誤!this 不是 User6.2 箭頭函式與 this
typescript
class Counter {
count = 0
// 箭頭函式綁定 this
increment = (): void => {
this.count++
}
}
const counter = new Counter()
const inc = counter.increment
inc() // OK,this 被正確綁定七、 泛型函式(預告)
typescript
// 泛型讓函式更靈活(詳見後續章節)
function identity<T>(value: T): T {
return value
}
const str = identity('hello') // string
const num = identity(42) // number八、 實用範例
8.1 事件處理器
typescript
type EventHandler<E extends Event> = (event: E) => void
function addClickListener(
element: HTMLElement,
handler: EventHandler<MouseEvent>
): void {
element.addEventListener('click', handler)
}
addClickListener(document.body, (event) => {
console.log(event.clientX, event.clientY)
})8.2 非同步函式
typescript
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
interface User {
id: number
name: string
}
// 使用
const user = await fetchUser(1) // User8.3 高階函式
typescript
function debounce<T extends (...args: any[]) => void>(
fn: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout>
return (...args) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn(...args), delay)
}
}
const debouncedLog = debounce((msg: string) => console.log(msg), 300)
debouncedLog('Hello')總結
| 語法 | 說明 | 範例 |
|---|---|---|
(a: T) => R | 函式類型 | (n: number) => string |
param? | 可選參數 | fn(a: string, b?: number) |
param = value | 預設值 | fn(a: string = 'hello') |
...rest | 剩餘參數 | fn(...args: number[]) |
| 重載 | 多種簽名 | 見上方範例 |
> **設計原則**:
- 參數越少越好
- 使用物件參數處理多參數
- 善用預設值減少調用複雜度
進階挑戰
- 實作一個
pipe函式,將多個單參數函式組合:
typescript
// pipe(fn1, fn2, fn3)(value)
// 等同於 fn3(fn2(fn1(value)))
const addOne = (n: number) => n + 1
const double = (n: number) => n * 2
const toString = (n: number) => String(n)
// 實作 pipe 函式
// const result = pipe(addOne, double, toString)(5)
// 結果應該是 '12'- 為以下函式加上重載,使其回傳精確類型:
typescript
function parse(input: string): object
function parse(input: object): string
// 實作...