今天要進入 TypeScript 型別系統中另一個很重要的領域——函式型別。
在 JS 裡,函式的參數型別、回傳值完全靠人自律;
在 TS 裡,我們可以用型別把它們鎖得死死的,少打很多莫名其妙的 bug。
function add(a: number, b: number): number {
return a + b;
}
a: number, b: number
→ 限制參數型別: number
→ 限制回傳值型別type AddFn = (a: number, b: number) => number;
const add: AddFn = (x, y) => x + y;
好處是可以重複使用型別,尤其適合多個函式簽名一致的情況。
interface AddFn {
(a: number, b: number): number;
}
const add: AddFn = (x, y) => x + y;
用 ?
表示該參數可選:
function greet(name: string, title?: string) {
if (title) {
console.log(`${title} ${name}`);
} else {
console.log(name);
}
}
greet("Marco");
greet("Marco", "Dr.");
function greet(name: string, title: string = "Mr./Ms.") {
console.log(`${title} ${name}`);
}
接收不定數量參數:
function sum(...numbers: number[]): number {
return numbers.reduce((acc, cur) => acc + cur, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
允許同一個函式有多種型別簽名:
function getValue(x: string): string;
function getValue(x: number): number;
function getValue(x: string | number): string | number {
return x;
}
let a = getValue("abc"); // 推論成 string
let b = getValue(123); // 推論成 number
注意:最後的實作必須能處理所有簽名的情況。
type HttpMethod = "GET" | "POST";
function request(url: string, method: HttpMethod, body?: any): Promise<any> {
return fetch(url, {
method,
body: body ? JSON.stringify(body) : undefined
}).then(res => res.json());
}
request("/api/users", "GET");
request("/api/users", "POST", { name: "Alice" });
TS 允許多傳參數,但會忽略多餘的:
function log(msg: string) {
console.log(msg);
}
log("Hello", "World"); // 第二個參數被忽略
建議:多參數情況要明確定義剩餘參數。
有時候推論就好,但在公共 API上要明確定義回傳型別,避免日後修改造成型別變動。
函式型別遇上泛型會更強大:
function identity<T>(value: T): T {
return value;
}
let num = identity(123); // T = number
let str = identity("Hello"); // T = string
泛型函式會在 Day 11 詳解。