TypeScript 函式 (Function)
函式是 JavaScript/TypeScript 程式的基本構建塊。TypeScript 在 JavaScript 函式的基礎上,加入了參數型別和回傳型別的標註功能,讓函式更加安全可靠。
函式宣告
具名函式 (Named Function)
function add(a: number, b: number): number {
return a + b;
}
console.log(add(1, 2)); // 3
函式表達式 (Function Expression)
const multiply = function (a: number, b: number): number {
return a * b;
};
console.log(multiply(3, 4)); // 12
箭頭函式 (Arrow Function)
const divide = (a: number, b: number): number => {
return a / b;
};
// 單一表達式可以省略大括號和 return
const subtract = (a: number, b: number): number => a - b;
console.log(divide(10, 2)); // 5
console.log(subtract(10, 3)); // 7
函式型別 (Function Type)
可以定義完整的函式型別:
// 定義函式型別
type MathOperation = (a: number, b: number) => number;
// 使用函式型別
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;
// 作為參數型別
function calculate(a: number, b: number, operation: MathOperation): number {
return operation(a, b);
}
console.log(calculate(10, 5, add)); // 15
console.log(calculate(10, 5, subtract)); // 5
可選參數 (Optional Parameters)
使用 ? 標記可選參數:
function greet(name: string, greeting?: string): string {
if (greeting) {
return `${greeting}, ${name}!`;
}
return `Hello, ${name}!`;
}
console.log(greet('小明')); // Hello, 小明!
console.log(greet('小明', '早安')); // 早安, 小明!
可選參數必須放在必要參數之後。
預設參數 (Default Parameters)
function greet(name: string, greeting: string = 'Hello'): string {
return `${greeting}, ${name}!`;
}
console.log(greet('小明')); // Hello, 小明!
console.log(greet('小明', '嗨')); // 嗨, 小明!
預設參數可以放在任何位置,但如果不是最後一個參數,呼叫時需要傳入 undefined:
function example(a: number = 10, b: number): number {
return a + b;
}
console.log(example(undefined, 5)); // 15
剩餘參數 (Rest Parameters)
使用 ... 語法收集多餘的參數:
function sum(...numbers: number[]): number {
return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3, 4, 5)); // 15
結合一般參數
function log(prefix: string, ...messages: string[]): void {
messages.forEach((msg) => console.log(`${prefix}: ${msg}`));
}
log('INFO', '系統啟動', '連線成功');
// INFO: 系統啟動
// INFO: 連線成功
函式重載 (Function Overloads)
當函式可以接受不同型別的參數並回傳不同型別時,使用重載:
// 重載簽名
function reverse(value: string): string;
function reverse(value: number[]): number[];
// 實作簽名
function reverse(value: string | number[]): string | number[] {
if (typeof value === 'string') {
return value.split('').reverse().join('');
}
return value.slice().reverse();
}
console.log(reverse('hello')); // "olleh"
console.log(reverse([1, 2, 3])); // [3, 2, 1]
更複雜的重載範例
function createElement(tag: 'a'): HTMLAnchorElement;
function createElement(tag: 'div'): HTMLDivElement;
function createElement(tag: 'span'): HTMLSpanElement;
function createElement(tag: string): HTMLElement;
function createElement(tag: string): HTMLElement {
return document.createElement(tag);
}
const anchor = createElement('a'); // HTMLAnchorElement
const div = createElement('div'); // HTMLDivElement
const other = createElement('custom'); // HTMLElement
this 參數
在 TypeScript 中,可以明確標註 this 的型別:
interface User {
name: string;
greet(this: User): void;
}
const user: User = {
name: '小明',
greet() {
console.log(`Hello, I'm ${this.name}`);
},
};
user.greet(); // Hello, I'm 小明
// 錯誤的使用方式
// const greet = user.greet;
// greet(); // 錯誤:'this' 的型別不正確
回呼函式中的 this
interface Clickable {
addClickListener(onClick: (this: void, e: Event) => void): void;
}
// this: void 表示回呼不應該使用 this
泛型函式 (Generic Functions)
泛型讓函式可以適用於多種型別:
function identity<T>(value: T): T {
return value;
}
console.log(identity<string>('hello')); // "hello"
console.log(identity<number>(42)); // 42
console.log(identity('world')); // TypeScript 自動推論 T 為 string
多個型別參數
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const result = pair('hello', 42); // [string, number]
泛型約束
interface HasLength {
length: number;
}
function getLength<T extends HasLength>(value: T): number {
return value.length;
}
console.log(getLength('hello')); // 5
console.log(getLength([1, 2, 3])); // 3
// console.log(getLength(123)); // 錯誤:number 沒有 length 屬性
回呼函式 (Callback Functions)
type Callback<T> = (result: T) => void;
function fetchData<T>(url: string, callback: Callback<T>): void {
// 模擬非同步操作
setTimeout(() => {
const data = {} as T;
callback(data);
}, 1000);
}
interface User {
name: string;
email: string;
}
fetchData<User>('/api/user', (user) => {
console.log(user.name); // TypeScript 知道 user 是 User 型別
});
高階函式 (Higher-Order Functions)
接受或回傳函式的函式:
// 回傳函式的函式
function multiplier(factor: number): (n: number) => number {
return (n) => n * factor;
}
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
接受函式的函式
function map<T, U>(array: T[], fn: (item: T) => U): U[] {
return array.map(fn);
}
const numbers = [1, 2, 3];
const strings = map(numbers, (n) => n.toString()); // ["1", "2", "3"]
void 與 undefined
void 和 undefined 作為回傳型別有細微差異:
// void:不在意回傳值
type VoidFunc = () => void;
const fn1: VoidFunc = () => {
return true; // OK,回傳值會被忽略
};
// undefined:必須明確回傳 undefined
type UndefinedFunc = () => undefined;
const fn2: UndefinedFunc = () => {
return undefined; // 必須這樣寫
};
never 型別
表示函式永遠不會正常結束:
// 拋出例外
function throwError(message: string): never {
throw new Error(message);
}
// 無限迴圈
function infiniteLoop(): never {
while (true) {}
}
實用範例
事件處理器
type EventHandler<E extends Event> = (event: E) => void;
const handleClick: EventHandler<MouseEvent> = (event) => {
console.log(event.clientX, event.clientY);
};
document.addEventListener('click', handleClick);
防抖函式 (Debounce)
function debounce<T extends (...args: any[]) => void>(
fn: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
const debouncedLog = debounce((message: string) => {
console.log(message);
}, 300);