iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0
Modern Web

Hello TypeScript 菜鳥系列 第 20

Day 19. TypeScript Generic 泛型:Generic Interface、Generic Class

  • 分享至 

  • xImage
  •  

前一篇已經透過一個很簡單的generic function例子學到一點點generic最基本語法:

function getSomething<T>(arg: T): T {
	return arg
}

雖然目前為止只學到使用一個型別變數(type variable)的generic function,但是一般情況下,一個型別變數其實已經很夠用,因為型別變數也可以搭配其他好用的參考型別,譬如之前學過的 Union

不過事實上,generic語法也允許有一個以上的型別變數:

function getMapElement<K, V extends keyof K>(map: Map<K, V>, key: K): V | undefined {
  return map.get(key);
}

這次範例為了展示多型別變數的generic函式而稍微複雜一點,其中的 extends keyof K 這段程式碼會在明天的文章說明,這邊可以暫時不管。

範例使用JavaScript ES6以後新增的MapMap 是一種類似於Object卻能儲存任意型別key-value pair的特殊容器。

雖然 Map 可以指定任意型別的 key 或 value,不過某些時候也許會想要指定 Map 的key或value只能是一種或幾種型別,因此範例刻意指定Map的key型別為K、value為V。

以上會特地說明generic可以有多個型別變數,是因為generic語法其實也可以用來建立generic interface和generic class,比起函式,generic interface和generic class可能更容易碰到需要多個型別變數的情境。


Generic Interface

先來看一下單一型別變數和多型別變數的generic interface語法,大致的語法如下:

// single type parameter
interface InterfaceName1<T> {
	...
}

// multiple type parameter
interface InterfaceName2<U, V> {
	...
}

Generic interface的語法和平時在使用的interface差不多,只是多了 <> 語法,以及要把型別替換成型別變數,如下範例:

interface Pair<K, V> {
	// properties
	key: K;
	value: V;
	
	// methods
	getValue(): V;
}

const pair: Pair<string, number> = {
	key: 'KEY',
	value: 12345,
	getValue(){
		return this.value;
	}
}

console.log(pair.getValue());

Generic Class

單一型別變數和多型別變數的Generic class則是如下:

class ClassName1<T> {
	...
}

class ClassName2<U, V> {
	...
}

和generic interface的狀況一樣,generic class語法也是和平時在寫class差不多,這次試著用generic class模擬非常陽春版的 Map

class MyMap<K, V> {
	private pairs: Record<string, V>;
  	private static mapSize = 0;
	
	constructor() {
    	this.pairs = {};
	}
	
	set(key: K, value: V) {
		this.pairs[JSON.stringify(key)!] = value;
		++MyMap.mapSize;
	}
	
	get(key: K) {
		return this.pairs[JSON.stringify(key)];
	}

    get size() {
		return MyMap.mapSize;
	}
}

const myMap = new MyMap<any, any>();

myMap.set(1, "Hello");
myMap.set(true, "Hello World");

console.log(myMap.size);		// 2
console.log(myMap.get(true));	// "Hello World"

關於類別有一點要特別注意,generic的型別變數無法用在靜態成員(static member),也就是無法用在靜態屬性(static properties)和靜態方法(static method)

generic的型別變數只能用在實例(instance)屬性和方法,所以上面範例的靜態屬性 size 暗地裡其實會被推導成 number 型別而非generic型別變數。

最後還需留意TypeScript沒有所謂的generic enums和generic namespaces,而其他型別的generic用法可以參考官方文件或是其他網路好文。


參考資料
Generic @TypeScript Handbook
TypeScript


上一篇
Day 18. TypeScript Generic 泛型:超基本語法
下一篇
Day 20. TypeScript Generic 泛型:Generic Constraints
系列文
Hello TypeScript 菜鳥31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言