TypeScript 的泛型化常用技巧

類型推導

目前 TS 的類型推導,仍不算完美。假定我們想做一個類似 useFetch / useLoader 這樣的封裝,思維的習慣會聚焦在 Query / Result 這兩個類型上(就是 Input/Ouput),但圍繞這兩個類型做函數參數或 Object 類型,來實現的類型推導體系(即在實際調用層,通過實際的參數,來取代泛型的聲明),目前在 TS 上是有一些問題的(不是做不到,是很繞也很繁瑣)。

在 TS 裏,更有效的做法,是關注載體(而不是關注端點)。比如:https://gitee.com/janpoem/use-the-loader/blob/master/src/useTheLoader.ts

即:type Loader<Q, R> = (Q) => Promise<R>。Loader 自身就負載了 Query / Result,然後通過 infer 即可提取出 Loader 的 Q/R。

這裏又有另外一個問題,即 Parameters 這個在類型推導中使用的侷限性。他無法有效的展開數組元素的類型推導。使用這個會讓你需要寫很多的 ts-ignore 和 eslint-disable-next-line。

迴歸 interface —— 泛型聲明過多

假定我們做一個數據視圖,這個視圖包含兩種以上截然不同數據結構。我們首先會習慣用泛型和類型推導做,這樣能做到,但存在的問題是,關聯的組件、函數、Class,都要一個一個套上對應的泛型聲明,非常麻煩,維護難度高。

這時候,可以迴歸到 interface 的方式上,即:

intreface BasicSheetData {
	type: string;
	// 基礎結構體
}

interface ASheetData extends BasicSheetData {
	type: 'a',
	// ASheet 結構體
}

interface BSheetData extends BasicSheetData {
	type: 'b',
	// BSheet 結構體
}

// 通過函數將類型推到成某個類型
const isASheetData = (d: BasicSheetData): d is ASheetData => d.type === 'a';
const isBSheetData = (d: BasicSheetData): d is BSheetData => d.type === 'b';

function usage(data: BasicSheetData): void {
	if (isASheetData(data)) {
		// 這 scope 裏,data 被推導成 ASheetData
		// do something for ASheetData
	}  else if (isBSheetData(data)) {
		// 這 scope 裏,data 被推導成 BSheetData
		// do something for BSheetData
	}
}

越到底層,越只需要關注 BasicSheetData。具體到具體類型,關注具體類型。公共組件,則通過 isXX 方法,來將 BasicSheetData 推導成特定類型即可。

這樣就能大大簡化了你的類型聲明(尤其批評國外現在各種 npm 庫,那個泛型滿天飛,比如 react-hook-form,本質上用不到那些泛型)。

到底是 type 還是 interface,是 , 還是 ;

回到 TS,我才發現我對 C 語言是有多偏執,我是對 type (struct 聲明方式)真的是情有獨鍾。

但在使用過程還是應該要注意,interface 能準確表達繼承的涵義,尤以 TS 這種泛型的聲明模式 (基於 extends ,不支持 Scala 那種),如果在類型設計存在繼承關係的,還是應該要用 interface

type T = P & { } 並不算做準確的類型繼承,這裏 T 和 P 是兩種類型,T 具有 P 部分屬性,彼此無關聯性(尤其是,interface 允許 override,type 的 & 會讓一個屬性具備兩種類型檢查,你必須明確聲明 Omit<P, 'prop'> & {})。

但,interface 和 type 之間,可以互相串,比如:

type T = {}

interface A extends T {}

type B = A & {}

其次是,type 和 interface 裏的字段聲明,到底用 , 還是 ;,我是習慣用 ,,主要是這樣的結構易於被複制、粘貼,或者是將某個 JS/JSON 結構,改寫爲類型聲明,這樣快。其實無區別,TS 最終都要轉譯,最終轉譯產物是 ;

兩者之間也可以共用共存,沒毛病,不必計較。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章