TypeScript 物件型別 (Object Types)
物件是 JavaScript 中用來組織資料最基本的方式。在 TypeScript 中,我們主要有三種方式來描述物件的「形狀」:匿名型別、Type Alias 和 Interface。
1. 匿名物件型別 (Anonymous)
這是最簡單直接的方式,就像我們不需要為每個變數取名字一樣,我們也可以不幫型別取名字。
function printCoord(pt: { x: number; y: number }) {
console.log(`座標:(${pt.x}, ${pt.y})`);
}
printCoord({ x: 3, y: 7 });
缺點:如果多個函式都要用這個座標型別,你就得複製貼上好幾次 { x: number; y: number },這很醜而且難維護。
2. Type Alias (型別別名)
我們可以用 type 關鍵字給這個「形狀」取個名字,方便重複使用。
type Point = {
x: number;
y: number;
};
// 現在可以用 Point 來代替那串長長的定義了
function printCoord(pt: Point) {
console.log(`座標:(${pt.x}, ${pt.y})`);
}
3. Interface (介面)
interface 是 TypeScript 特有的關鍵字,專門用來定義物件的結構。
interface Point {
x: number;
y: number;
}
function printCoord(pt: Point) {
console.log(`座標:(${pt.x}, ${pt.y})`);
}
新手疑惑:Type 和 Interface 差在哪?
對於定義物件來說,它們99% 的情況下是可以混用的。
- Interface 比較像「合約」,它支援自動合併 (Declaration Merging),通常擴充性比較好,適合用來定義 API 回傳格式或第三方套件的設定。
- Type 也非常強大,雖然不能自動合併,但它能定義更靈活的型別(如 Union Types),這是 Interface 做不到的。
結論:喜歡哪個用哪個,保持專案一致就好。如果真的猶豫不決,先用
interface。
物件屬性的眉眉角角
可選屬性 (Optional Properties)
有些屬性不一定每次都有,這時可以用 ?。
interface User {
name: string;
age?: number; // 不一定要填 age
}
const user1: User = { name: 'Alice' }; // OK
const user2: User = { name: 'Bob', age: 18 }; // OK
唯讀屬性 (Readonly Properties)
如果你希望某個屬性在初始化後就不能再被修改,加上 readonly。
interface Point {
readonly x: number;
readonly y: number;
}
const p: Point = { x: 10, y: 20 };
// p.x = 5; // 錯誤!x 是唯讀的
這在 React 的 props 或 Redux 的 state 定義中非常常見,因為我們通常希望這些資料是不可變的 (Immutable)。
索引簽名 (Index Signatures)
當你不知道物件會有什麼屬性,只知道「屬性名是字串,值是數字」時,你可以這樣寫:
interface SalaryMap {
[name: string]: number;
}
const salaries: SalaryMap = {
Alice: 50000,
Bob: 45000,
Charlie: 60000,
// 可以無限加下去...
};