在 TypeScript 中,我們可以使用 interface
關鍵字來聲明自定義型別,用來描述物件的結構和形狀,以及物件應該具備的屬性和方法。我們可以使用 interface 來聲明變數、函數參數、函數返回值等,以確保相應的物件符合interface 的結構。和 type 一樣,威爾豬喜歡在 interface 加上前綴字 I
來區分這是使用 interface 聲明的型別。(一樣官方不推薦加上前綴就是~
)
interface 到底應該翻譯為接口還是介面,威爾豬自己也不清楚,兩種說法都有人使用,但官方的簡體中文翻譯為接口,所以威爾豬就以接口來表示 interface 了。
通常我們會使用 interface 來 聲明物件應該具有哪些屬性和屬性類型
,看以下範例:
interface IPerson {
name: string;
age: number;
job: string;
}
const person: IPerson = {
name: "威爾豬",
age: 3,
job: "首富",
};
const greet = (person: IPerson): string =>
`${person.name},你才 ${person.age} 歲就成為了${person.job}`;
console.log(greet(person)); // 輸出: 威爾豬,你才 3 歲就成為了首富
注意:與 type 寫法稍有不同,聲明 interface 不需使用
=
。
我們也經常會使用到巢狀物件的結構,那該如何聲明呢?
interface IStudent {
name: string;
age: number;
courses: {
math: number;
design: number;
history: number;
};
}
const student: IStudent = {
name: "威爾豬",
age: 5,
courses: {
math: 65,
design: 95,
history: 80,
},
};
console.log(student.courses.design); // 輸出: 95
在這個例子中,我們使用 IStudent
接口聲明了物件的型別,並在其中嵌套了一個 courses
屬性,該屬性本身也是一個物件。
interface 還可以聲明可選屬性 ?
和唯讀屬性 readonly
,使物件的聲明更具靈活性和約束性。
interface IBook {
title: string;
author: string;
year: number;
isEbook?: boolean; // 可選屬性
readonly ISBN: string; // 唯讀屬性
}
const book: IBook = {
title: "用 30 天學會基本 TypeScript",
author: "威爾豬",
year: 2023,
ISBN: "ABC-1234567890",
};
在上面的例子中,isEbook 是一個可選屬性,可以有也可以沒有。而 ISBN 是唯讀屬性,一旦賦值就不能修改。
在 TypeScript 中,物件還可以包含其他物件作為其屬性,看以下範例:
interface IAddress {
street: string;
city: string;
zipCode: string;
}
interface IContact {
email: string;
phone: string;
}
interface IPerson {
name: string;
address: IAddress;
contact: IContact;
}
const person: IPerson = {
name: "威爾豬",
address: {
street: "皇后大道 123 街",
city: "墨爾本",
zipCode: "001",
},
contact: {
email: "willPig@example.com",
phone: "0498765432",
},
};
IPerson 接口裡,包含了 IAddress 和 IContact 接口的屬性。
除了描述物件的結構,interface 還可以描述函式的結構和型別。這對於函式的參數和返回值進行型別檢查和代碼提示非常有用。以下是在 interface 中使用函式型別的範例:
interface ICalculator {
(a: number, b: number): number;
}
const increase: ICalculator = (a, b) => a + b;
const decrease: ICalculator = (a, b) => a - b;
console.log(increase(2, 3)); // 輸出: 5
console.log(decrease(2, 3)); // 輸出: -1
在這個例子中,我們定義了一個 ICalculator 接口,它描述了接受兩個參數並返回 number 型別的函式型別。
接口可以繼承其他接口,從而將多個接口的屬性和方法結合在一個接口中。還記得 type 是用 &
來進行擴展吧,而 interface 就是直接使用 extends
來實現繼承,我們看以下範例:
interface IAnimal {
name: string;
sounds: string;
}
interface IBird extends IAnimal {
fly(): void;
}
const eagle: IBird = {
name: "小白鷹",
sounds: "嘰嘰喳喳",
fly() {
console.log("我會飛!");
},
};
const cat: IAnimal = {
name: "小藍貓",
sounds: "喵喵",
};
在上面的例子中,IBird 接口繼承了 IAnimal 接口,並添加了一個 fly 方法。這樣就 只有定義為 IBird 接口的動物才會有 fly 這個方法
可以使用。
當然我們也可以實現多繼承,以下範例:
interface IWidth {
width: number;
}
interface IHeight {
height: number;
}
interface IRect extends IWidth, IHeight {
color: string;
}
let rect: IRect = {
width: 100,
height: 100,
color: "red",
};
上面的例子即為 IRect
接口同時繼承了 IWidth
和 IHeight
兩個接口。
在 TypeScript 中,類別可以使用 implements
來實現接口,表示該類別符合接口聲明的契約。以下是在類別中實現接口的範例:
interface IShape {
getArea(): number;
}
class Circle implements IShape {
constructor(private radius: number) {}
getArea() {
let area = Math.round(Math.PI * this.radius ** 2 * 100) / 100;
console.log(area); // 輸出: 314.16
return area;
}
}
const circle = new Circle(10);
circle.getArea();
上面的例子,我們聲明了一個 IShape 接口,它有一個 getArea 方法。然後,我們創建了一個 Circle 類別,並實現 Shape 接口的要求。
我們也可以使用泛型來創建具有彈性的接口,能夠應用於多種型別。泛型接口允許我們在內部使用型別參數,以便於後續指定具體的型別。以下是使用泛型接口的範例:
interface IGeneric<T> {
value: T;
}
const stringBox: IGeneric<string> = { value: "威爾豬" };
const numberBox: IGeneric<number> = { value: 3 };
在這個例子中,我們定義了一個泛型接口 IGeneric<T>
,它有一個 value 屬性,型別由泛型參數 T
決定。然後,我們創建了兩個 Box 物件,一個是 string 型別的,另一個是 number 型別的。
如果 聲明同樣名稱的接口
,只要在同一個 .ts 文件下,不管離得多遠,接口就會 自動合併
,並不會像 type 一樣出現錯誤。不過除非不得已,不然威爾豬自己比較少這麼做,感覺程式碼一多會顯得混亂,看以下範例:
interface IAnimal {
name: string;
}
interface IAnimal {
sound: string;
}
const dog: IAnimal = {
name: "小灰狗",
sound: "汪汪",
};
上面範例就是聲明了兩個一樣名稱為 IAnimal 的接口,一個有 name 屬性,一個有 sound 屬性,而 IAnimal 型別的 dog 就會自動包含了兩種屬性。
總之,interface 不僅能夠應用於物件,還可以描述函式型別、類別的結構和繼承關係,使用接口的主要目的是:
定義物件的結構和形狀,確保物件符合特定的結構要求
定義函數的型別,包括參數和返回值的結構
提高程式碼的可讀性和可維護性
看到這邊肯定會覺得 type 和 interface 使用上感覺大同小異,那它們有什麼不同或區別呢?又應該什麼時候使用哪一種方式?這我們之後的章節再來討論。