TypeScript: Generic Types and Type Alias
此篇要來談談 TypeScript 通用型別 (Generic Types) 的用途,以及 interface 和型別別名 (Type Alias) 的使用差異。
通用型別 (Generic Types)
有時我們想在具有多種型別可能的情況下重用 (reuse) 程式碼,例如以下這個例子:
1function identity(arg: number): number {
2 return arg;
3}
identify
函式收到參數 arg
,並回傳該參數。如果想更有彈性地使用函式,比方說不限定參數型別,則我們可以使用 any
:
1function identity(arg: any): any {
2 return arg;
3}
但如此一來我們便無法限制回傳值的型別要等同輸入值的型別,這時正是使用通用型別的時機。
我們使用 type variable T
來代表型別,限制了函式輸入和輸出值的型別必須相同:
1function identify<T>(arg: T): T {
2 return arg;
3}
使用 type variable 時,因為無從事先得知變數的型別,所以無法操作屬性或方法,例如以下範例將會收到錯誤:
1function identify<T>(arg: T): T {
2 console.log(arg.length); // error: Property 'length' does not exist on type
3 return arg;
4}
不過如果是:
1function loggingIdentity<T>(args: T[]): T[] {
2 console.log(args.length);
3 return args;
4}
將不會有問題,因為 arrays of type T
確定有 length
屬性。
通用型別當然也可以跟 interface, class, tuple 等搭配運用,詳細使用方式請見官方文件。
介面 (Interface) 與型別別名 (Type Alias) 的異同
型別別名在使用上和 interface 非常相似。
相似之處
type
和 interface
一樣可以用來定義物件格式:
1type Animal = {
2 name: string
3}
type
可使用 intersections 延伸屬性(interface
則通常會使用 extends
)
1type Animal = {
2 name: string
3}
4
5type Bear = Animal & {
6 honey: boolean
7}
8
9const bear = getBear();
10bear.name;
11bear.honey;
不同之處
Aliasing doesn’t actually create a new type - it creates a new name to refer to that type. Aliasing a primitive is not terribly useful, though it can be used as a form of documentation.
根據官方文件,type
看起來更推薦用在單純要表示偏靜態的資料格式之時:
1// declaration
2type GreetingLike = string | (() => string) | MyGreeter;
3
4declare function greet(g: GreetingLike): void;
5
6// usage
7function getGreeting() {
8 return "howdy";
9}
10
11class MyGreeter extends Greeter {
12 ...
13}
14
15greet("hello");
16greet(getGreeting);
17greet(new MyGreeter());