Typescript 泛型

泛型,字面上看就是寬泛的類型約束。是指在定義函數、接口或類的時,不指定類型,在使用時指定類型(runtime)。git

使用場景

考慮到重用性和擴展性,由於組件除了支持當前數據類型,還要考慮將來須要支持新的類型,這是一個合格程序員應有的素養和一個好系統的衡量標準之一(功能的靈活性)。程序員

泛型變量

設計一個函數:輸入什麼類型,輸出也是什麼類型github

// generics.ts
function same(arg: number): number {
    return arg;
}
console.log(same(18)); // 18
複製代碼

這個只適合數字類型,換成字符串就得再寫一個,無重複性可言,那就得改造typescript

// generics2.ts
function same2(arg: any): any {
    return '十八';
}
console.log(same2(18)); // 十八
複製代碼

沒報錯,可是違背了輸入類型和輸出類型相同這個要求。數組

重點來了,咱們須要markdown

// generics3.ts
function same3<T>(arg: T): T {
    return '十八';
}
console.log(same3(18));

function same3_1<T>(arg: T): T {
    return arg;
}
console.log(same3_1<number>(18));
console.log(same3_1('十八'));


// 0.1.2/generics3.ts:2:5 - error TS2322: Type '"十八"' is not assignable to type 'T'.
    // '"十八"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
    // 2 return '十八';
複製代碼

剖析下函數

  • 例子中在函數名 same3 後面添加 <T>(可理解爲定義類型變量,你也能夠叫 S);
  • T 自己理解爲類型變量,輸入類型(18十八)時就是賦值這個類型變量是什麼類型(對應 numberstring);
  • 後面能夠繼續使用這個類型變量 T 來約束輸出類型;
  • 函數使用時,可在函數名後強制定義輸入類型 same3_1<number>(18);
  • 函數使用時,也能夠不強制定義輸入類型 same3_1('十八'),這是利用類型推論(編譯器根據輸入類型自動幫助捕獲 T的類型),很顯然這個使用簡單,推薦使用;
  • 最後,函數 same3_1 能夠適合任何類型,管它叫泛型也是實至名歸,恭喜你 same3_1;

多個類型變量

有時,參數也不止一個,多個狀況下oop

// generics4.ts
function info<S, N>(name: S, age: N): [S, N] {
    return [name, age];
}

console.log(info('pr', 18)); // [ 'pr', 18 ]
複製代碼

泛型類型

// generics4_1.ts
const generics4_1 = <T>(arg: T): T => {
    return arg;
}

let generics4_1_1: <S>(arg: S) => S = generics4_1;

console.log(generics4_1(18)); // 18
console.log(generics4_1_1('十八')); // 十八
複製代碼
  • 泛型函數的類型與非泛型函數的類型區別在於:是否有一個類型參數在最前面 <T>、<S>
  • 可使用不一樣的泛型參數名 T、S,只要數量上和使用方式上對應上就行了;

泛型接口

// generics4_2.ts
interface Generics4_2 {
    <S>(arg: S): S;
} 

const generics4_2 = <T>(arg: T): T => {
    return arg;
}

let generics4_2_1: <T>(arg: T) => T = generics4_2;
let generics4_2_2: { <S>(arg: S): S } = generics4_2; // 對象字面量
let generics4_2_3: Generics4_2 = generics4_2; // 泛型接口

console.log(generics4_2(18)); // 18
console.log(generics4_2_2('十八')); // 十八
console.log(generics4_2_2('pr')); // pr
複製代碼
  • 可使用帶有調用簽名的對象字面量 { <S>(arg: S): S } 來定義泛型函數;
  • 可使用泛型接口 Generics4_2 來定義泛型函數;

泛型類

其實和泛型接口很像,一塊兒看看,順便對比下post

// genericsClass.ts
class Sum <T>{
    zero: T;
    add: (x: T, y: T) => T
}

const sum = new Sum<number>();
sum.zero = 0;
sum.add = (x, y) => (x + y);
console.log(sum.add(5, 6)); // 11

const sum1 = new Sum<string>();
sum1.zero = '0';
sum1.add = (x, y) => (x + y);
console.log(sum1.add('5', '6')); // 56
複製代碼
  • 經過 sumsum1 你會發現不單單限爲 numberstring 也能夠;
  • 與接口同樣,泛型類型放在類後面,能夠直觀地確認類的屬性都在用相同類型;

泛型約束

上面例子的函數體過於簡單,對於參數的屬性或方法並無使用。因爲咱們事先並不知道輸入參數是什麼類型,不能貿然使用參數的屬性,好比輸入參數是數字,咱們知道數字沒有 length 屬性,看看 Typescript 給咱們報什麼錯(有點找茬的意思哈)。ui

// generics5.ts
function same5<T>(arg: T): T {
    console.log(arg.length);
    return arg;
}
console.log(same5(18));
console.log(same5('十八'));

// 0.1.2/generics5.ts:2:21 - error TS2339: Property 'length' does not exist on type 'T'.
    // 2 console.log(arg.length);
複製代碼

如咱們推測的同樣,泛型 T 在不知道什麼類型的狀況下不知道是否包含屬性 length,因此編譯報錯了。

那如何解決呢?萬能的接口能夠幫助咱們

// generics6.ts
interface Length {
    length: number;
}

function same6<T extends Length>(arg: T): T {
    console.log(arg.length);
    return arg;
}
console.log(same6(18));
console.log(same6('十八'));
console.log(same6(['pr', '30', 'boy']));

// 0.1.2/generics6.ts:9:19 - error TS2345: Argument of type '18' is not assignable to parameter of type 'Length'.
    // 9 console.log(same6(18));
複製代碼

解釋下

  • 使用 extends 繼承接口 Length 來約束泛型 T 符合接口屬性,說人話就是輸入的類型必須包含 length 屬性了
  • 字符串 十八 和數組 ['pr', '30', 'boy'] 符合要求,數字 18 明顯不符合要求了;

那多個參數的泛型約束怎麼處理?

// generics7.ts
function info2<S extends N, N>(name: S, age: N): [S, N] {
    return [name, age];
}

console.log(info2('pr', 18));
console.log(info2(30, 18));


// 0.1.2/generics7.ts:5:19 - error TS2345: Argument of type '"pr"' is not assignable to parameter of type 'number'.
    // 5 console.log(info2('pr', 18));
複製代碼

報錯緣由在於,參數 pr 不能分配給類型 number 的參數。

本次代碼 Github

你能夠...

上一篇:Typescript 別名和字符串字面量類型

下一篇:Typescript 聲明文件

目錄:Typescript 小書之入門篇

相關文章
相關標籤/搜索