TypeScript真香系列的內容將參考中文文檔,可是文中的例子基本不會和文檔中的例子重複,對於一些地方也會深刻研究。另外,文中一些例子的結果都是在代碼沒有錯誤後編譯爲JavaScript獲得的。若是想實際看看TypeScript編譯爲JavaScript的代碼,能夠訪問TypeScript的在線編譯地址,動手操做,印象更加深入。javascript
TypeScript的泛型與其餘面向對象的語言中的定義是類似的。泛型能夠理解爲在咱們定義函數、接口或者類的時候,不預先指定其相關的類型,而是在使用的時候手動指定類型。這和TypeScript基礎類型中的any是有區別的。java
當咱們不使用泛型的時候,可能就以下這樣:git
function show(name: string): string {
return name;
}
function show(name: any): any {
return name;
}
複製代碼
上面例子中用any或許沒有什麼問題,可是當咱們想傳入的參數類型和返回值類型相同時,使用any時的返回值就可能有多種狀況了。還有就是咱們平時也會考慮重用性,提取公共組件等。這時泛型就能派上了。下面是一個泛型函數的例子:github
function show<T>(arg: T): T {
return arg;
}
複製代碼
這個show函數後面的 T ,叫作類型變量,這個咱們後面會介紹。這個函數咱們能夠用兩種方法調用。 第一種爲傳入參數,包括類型參數:typescript
show<string>("james"); //"james"
複製代碼
第二種爲不包括類型參數,這種方式利用了類型推論,這個再後面的章節會講到:數組
show(1); //1
複製代碼
上面所寫的例子中,咱們能夠在「<>」中指定一個變量T,而且咱們要把這個T定義在參數和返回值上。編譯器在檢查咱們的代碼的時候,咱們必須在函數體中正確的使用這個通用的類型。函數
function show<T>(arg: T): T {
console.log(arg.length) //錯誤,類型T不存在length屬性
return arg;
}
複製代碼
上面的例子中,咱們在打印arg的length中報錯了。緣由是咱們在使用這個函數的時候,傳入的參數可能爲數字,而數字是沒有length屬性的。 固然,咱們在調用函數的時候也要正確調用:ui
function show<T>(arg: T): T {
return arg;
}
show<string>(1); //錯誤,參數1的類型不是string
show<>(1); //錯誤,參數類型不能爲空
複製代碼
針對數組:this
function show<T>(arg: T[]): T[] {
return arg;
}
show<string>(["奧","利","給"]); //["奧", "利", "給"]
function show<T>(arg: Array<T>): Array<T> {
return arg;
}
show<number>([6, 6, 6]); // [6, 6, 6]
複製代碼
以下所示:spa
interface IMan {
<T>(arg: T): T
}
function man<T>(arg: T): T{
return arg;
}
let showMan: IMan = man;
showMan("james"); //"james"
複製代碼
還有另一種寫法,這種方法咱們能夠把泛型參數當作整個接口的參數:
interface IMan<T> {
(arg: T): T
}
function man<T>(arg: T): T{
return arg;
}
let showMan: IMan<string> = man;
showMan("james"); //"james"
複製代碼
這樣的好處就是在使用時知道具體的參數類型了。
咱們可使用(<>)和尖括號中間的變量,而後跟在類名的後面來表示泛型類。須要注意的是,泛型類指的是實例部分的類型,類的靜態屬性不能使用泛型類型。
class Man<T> {
name: T;
constructor(arg: T) {
this.name = arg;
}
play(a: T) : T {
return a;
}
}
let showMan = new Man<string>("James");
showMan.name; //"James"
showMan.play("ball"); //"ball"
複製代碼
也能夠稍微複雜一點:
class Man<T> {
name: T;
sum: T[];
constructor(arg: T) {
this.name = arg;
this.sum = [];
}
push(item: T): void{
this.sum.push(item)
}
output() {
return this.sum;
}
}
let showMan = new Man<string>("James");
showMan.push("one for all");
showMan.output(); //["one for all"]
複製代碼
讓咱們回到泛型變量一節中的一個例子:
function show<T>(arg: T): T {
console.log(arg.length) //錯誤,類型T不存在length屬性
return arg;
}
複製代碼
若是咱們想要使用length屬性,那該怎麼辦?能夠看下面的例子:
interface ILen {
length: number;
}
function show<T extends ILen>(arg: T): T {
console.log(arg.length) //沒有報錯了
return arg;
}
複製代碼
雖然上面的例子沒有再報錯,可是由於這個泛型函數被定義了約束,因此咱們再調用函數的時候又有了講究,也就是咱們在傳值的時候須要像下面的例子同樣:
interface ILen {
length: number;
}
function show<T extends ILen>(arg: T): T {
console.log(arg.length)
return arg;
}
show(3); //錯誤,參數3不能分配給類型ILen
show({ length: 2 }); // 2 {length: 2}
show({length:2, arg:1}) //2 {length: 2, arg: 1}
show({length:2, arg:"james"}) //2 {length: 2, arg: "james"}
複製代碼
文中有些地方可能會加入一些本身的理解,如有不許確或錯誤的地方,歡迎指出~