本文目錄:
- 1.什么是泛型
- 2.使用泛型變量
- 3.泛型接口
- 4.泛型類
- 5.泛型約束
- 6.裝飾器
1.什么是泛型
泛型的定義:
在定義函數(shù)脓钾、接口或者類的時候,不預先指定具體的類型越锈,而是在使用的時候再指定類型的一種特性仗嗦。
泛型的優(yōu)點:
提高代碼可重用性,使用泛型來創(chuàng)建可重用的組件甘凭,一個組件可以支持多種類型的數(shù)據(jù)稀拐。
我們有一個需求, 要定義一個函數(shù)对蒲,這個函數(shù)返回的值類型和傳入的參數(shù)的類型一致
function f(arg: number): number {
return arg;
}
但是這個函數(shù)只能傳入number類型钩蚊,如果要傳入字符串,必須要改函數(shù)蹈矮,或者換成any類型也可以達到效果砰逻,但是就失去了一些重要的類型信息,體會不到ts帶來的好處泛鸟。 這個時候我們使用泛型蝠咆,就可以很好的實現(xiàn)這個需求
function f<T>(arg: T): T {
return arg;
}
T 是類型變量(也可以叫類型參數(shù)),它是一種特殊的變量北滥,只用于表示類型而不是值刚操; 可以是任意字符 例如 U M都可以。幫助我們捕獲用戶傳入的類型
泛型函數(shù)的兩種使用方式:
第一種方式再芋,編譯器能夠自動地推斷出類型菊霜,這種方式更普遍一些
let v1 = f(123)
let v2 = f('字符串')
鼠標懸停上去,就可以分別得到 返回類型 number 和string
第二種方式济赎,傳入所有的參數(shù)鉴逞,包含類型參數(shù)
let v3 = f<boolean>(false)
let v4 = f<string>('hello')
我們把這個函數(shù)f叫做泛型,因為它可以適用于多個類型构捡;不同于使用any,它不會丟失信息
2.使用泛型變量
將類型變量(也可以叫類型參數(shù)壳猜,泛型變量)當做一個類型使用
function fn1<U>(argc: U[]): U[] {
console.log(argc.length);
return argc;
}
fn1([1, 2, 3, 4, 4]);
在上面的代碼中勾徽,泛型函數(shù)定義了一個類型變量T, 將這個類型變量當做我們類型類使用统扳; 我們接收的參數(shù)是T類型的數(shù)組喘帚,返回的也是T類型的數(shù)組畅姊,這個T可以是任意類型;增加了程序的靈活性
使用多個泛型變量
泛型變量T使我們常用的一個字符吹由,可以是任意字符涡匀,可以是多個字符
我們現(xiàn)在要寫一個函數(shù),交換任意兩個類型組成的元組類型
function swap<T, U>(param: [T, U]): [T, U] {
return [param[0], param[1]];
}
swap([1, 'a']);
swap([[1, 2, 3], { name: 123 }]);
3.泛型接口
就是將泛型的類型變量和我們的接口結(jié)合起來溉知,讓接口可以支持多種類型,更加靈活
我們使用之前學習過的函數(shù)表達式的方式創(chuàng)建一個函數(shù)
let fn3 = function(x: string, y: string): string[] {
return [x, y];
};
這個fn3函數(shù)的類型我們沒有定義腕够,是利用的 類型推論自動獲取的级乍,現(xiàn)在使用接口來定義一個符合我們這個函數(shù)需要的形狀
interface MyFn {
(x: string, y:string): string[]
}
// 這個時候就可以聲明一個帶類型的函數(shù)
let fn3:MyFn;
這個類型再修改一下,增加接口的復用性帚湘,將參數(shù)string換成動態(tài)的玫荣,由使用者決定;那么我們就需要使用泛型
interface MyFn {
<T>(x: T, y: T):T[]
}
let fn3:MyFn;
到這里我們的這個函數(shù)接口形狀就已經(jīng)完成大诸,還可以將泛型參數(shù)提升到我們的接口名稱上
interface MyFn<T> {
(x: T, y:T): T[]
}
let fn3:MyFn;
4.泛型類
泛型類看上去與泛型接口差不多捅厂。 泛型類使用( <>
)括起泛型類型,跟在類名后面资柔。用于類的類型定義
類有兩部分:靜態(tài)部分和實例部分焙贷。 泛型類指的是實例部分的類型,所以類的靜態(tài)屬性不能使用這個泛型類型贿堰。
與接口一樣辙芍,直接把泛型類型放在類后面,可以幫助我們確認類的所有屬性都在使用相同的類型class GenerNum<T>
class GenerNum<T> {
zero: T;
add: (x: number, y: T) => T;
}
和接口一樣羹与,在使用這個類的的時候故硅,還得傳入一個類型參數(shù)來指定泛型類型
let myGeNum = new GenerNum<string>();
myGeNum.zero = '0';
myGeNum.add = function(x, y) {
return x.toString() + y;
};
5.泛型約束
在函數(shù)內(nèi)部使用泛型變量的時候,由于事先不知道它是哪種類型纵搁,所以不能隨意的操作它的屬性或方法
語法: 使用 泛型變量T extends 繼承 我們定義的接口吃衅; 約束了必須符合的形狀
我們通過一個代碼來解釋什么是類型約束
function f3<U>(arg: U): U {
console.log(arg.length);
return arg;
}
上面的代碼中,泛型 T 不一定包含屬性 length腾誉,所以編譯的時候報錯了
我們可以對泛型進行約束徘层,只允許這個函數(shù)傳入那些包含 length 屬性的變量。這就是泛型約束
interface LengthIn {
length: number;
}
function f3<U extends LengthIn>(arg: U): U {
console.log(arg.length);
return arg;
}
如果輸入數(shù)字123妄辩,直接編譯報錯:類型“123”的參數(shù)不能賦給類型“LengthIn”的參數(shù)
// console.log(f3(123));
console.log(f3('hello'));
console.log(f3([1, 2, 3]));
console.log(f3({length:12, value: '測試'}));
6.裝飾器
隨著TypeScript
和ES6里引入了類惑灵,在一些場景下我們需要額外的特性來支持標注或修改類及其成員。 裝飾器(Decorators)為我們在類的聲明及成員上通過元編程語法添加標注提供了一種方式
若要啟用實驗性的裝飾器特性眼耀,你必須在命令行或tsconfig.json
里啟用experimentalDecorators
編譯器選項
裝飾器是一種特殊類型的聲明英支,它能夠被附加到類聲明,方法哮伟, 訪問符干花,屬性或參數(shù)上妄帘。它可以在不修改代碼自身的前提下,給已有代碼增加額外的行為
裝飾器使用 @expression這種形式池凄,expression求值后必須為一個函數(shù)抡驼,它會在運行時被調(diào)用,被裝飾的聲明信息做為參數(shù)傳入
有 5 種裝飾器:類裝飾器肿仑、屬性裝飾器致盟、方法裝飾器、訪問器裝飾器尤慰、參數(shù)裝飾器馏锡;我們這里只簡單的介紹一下前兩種(這也是在angular里面大量使用的兩種) 類裝飾器、屬性裝飾器
裝飾器寫法: 普通裝飾器(無法傳參)伟端, 裝飾器工廠(可以傳參)杯道,一般都是這種方式; 給修飾器加上參數(shù)责蝠,或者叫做'注解'党巾,或者叫元數(shù)據(jù) (元數(shù)據(jù)編程)
我們先來看第一種普通裝飾器
1.類裝飾器
沒有參數(shù)的裝飾器,類裝飾器的函數(shù)的參數(shù)就是 當前類的構(gòu)造函數(shù)本身
function Component(param) {
console.log('這是裝飾器');
console.log(param);
}
我們創(chuàng)建一個SideBar的側(cè)邊欄的組件類霜医, 用組件裝飾器修飾這個類
@Component
class SideBar {}
裝飾器對類的行為的改變齿拂,是代碼編譯時發(fā)生的(不是TypeScript編譯,而是js在執(zhí)行機中編譯階段)肴敛,而不是在運行時创肥。這意味著,修飾器能在編譯階段運行代碼值朋。也就是說叹侄,裝飾器本質(zhì)就是編譯時執(zhí)行的函數(shù)。
類裝飾器里面就只有一個參數(shù)昨登, 值就是被裝飾的類的構(gòu)造函數(shù)
利用函數(shù)柯里化解決傳參問題趾代, 向裝飾器傳入一些參數(shù),也可以叫 參數(shù)注解
function Component(param) {
return function(target) {
console.log('這是可以傳參的裝飾器');
// 這個param就是裝飾器的元數(shù)據(jù)丰辣,外界傳遞進來的參數(shù)
console.log(param);
console.log(target);
};
}
在使用裝飾器的時候
這個裝飾器裝飾緊跟在后面的類并增加一些屬性撒强,同時為其指定元數(shù)據(jù)
@Component({
templateUrl: 'aaaa',
styleUrl: 'bbb'
})
class SideBar {}
屬性裝飾器
屬性裝飾器表達式會在運行時當做函數(shù)被調(diào)用,有兩個參數(shù)
第一個參數(shù): 對于靜態(tài)成員來說是 構(gòu)造函數(shù)笙什; 對于實例成員來說是原型對象; 第二個參數(shù): 當前屬性的名稱
function Input(param?: any) {
return function(target, attr) {
// 屬性裝飾器里面的target 就是類的原型對象飘哨, 和類裝飾器的第一個函數(shù)不一樣
// 就是說在 類裝飾器里面 target就是構(gòu)造函數(shù)A,屬性裝飾器里面 target就是A.prototype
console.log(target, attr);
// 第二個參數(shù)attr就是 當前屬性名; 我們可以設置為可選參數(shù)
console.log(param);
};
}
class SideBar {
@Input() public list1: any;
@Input('self-defined') public list: any;
}