TS: 泛型

學 Java 的時候總會提到泛型,現在 TS 也有了,他們的用法都差不太多。泛型可以理解為廣泛的型別。
為什麼要用泛型
先來了解下泛型有什麼用。先看下面定義的一個函式:
function itself(a: number): number { return a } itself(1) // 1
上面的函式就是簡單的傳入一個數,返回一個數,但是現在想傳入字串,返回字串呢?或者再加傳入陣列,返回陣列呢?這就需要用到泛型了。
function itself<T>(a: T):T { return a } console.log(itself(true)) // true console.log(itself(1))// 1 console.log(itself('1')) // '1' console.log(itself([1, 2, 3])) // [1, 2, 3]
從上面的例子可以看到,T 有點像是一個臨時變數一樣,先推斷出傳入引數的型別,將這個型別賦值到 T T = XXX 型別
,後面就直接 返回的型別 = T = XXX 型別
。這麼說可能不那麼準確,但是可以看到 T 就是一個佔位符。
如果呼叫函式時使用顯式宣告可能會更直觀。
function itself<T>(a: T):T { return a } console.log(itself<boolean>(true)) // true console.log(itself<number>(1))// 1 console.log(itself<string>('1')) // '1' console.log(itself<Array<number>>([1, 2, 3])) // [1, 2, 3
這裡發現 Array<number>
裡也是用了泛型的,這樣可以宣告數組裡元素的型別。
介面與泛型
上面都是傳基本型別(除了 Array),下面看看介面與泛型的配合。
interface Human { name: string age: number } interface Animal { category: string } function create<T>(what: T): T { return what } create<Human>({ name: 'Jack', age: 18 }) create<Animal>({ category: 'dog' })
用法幾乎一樣,Easy~。
有了介面這個東西后,我們可以玩更高階一點的。現在我想造一個 Human,規定這個 Human 一定要有 JJ,可以這麼寫。
interface JJ { jjSize: string jjLength: number } interface Human { name: string age: number } function create<T extends JJ>(what: T): T { return what } create({ name: 'Jack', age: 18, jjSize: 'large', jjLength: 18 }) create({ name: 'Jack', age: 18 }) // 報錯:沒有 jjSize 和 jjLength
上面的這個用法叫做 泛型的約束 ,像剛剛說的這個 Human 一定要有 JJ,這就是一個約束。
類與泛型
說完介面,肯定逃不了要說類了。我們先來看一段 JS 的程式碼。
function create(C) { return new C() }
正確的理解應該是傳入一個建構函式 C,然後返回 C 的例項,沒了。但是這個函式啥約束都沒有,如果傳入一個字串 "Hello" 呢?那不是炸了?所以我們要用 TS 裡的泛型去約束它。
function create<T>(C: { new () }) { return new C() }
兩個括號裡寫的是 new ()
說明傳入的 C 是可以用 new C()
的。然後我們再加個上返回型別和傳入的型別,可以寫成這樣。
function create<T>(c: { new (): T }): T { return new c() }