本篇將介紹TypeScript裏的類和接口。json
與其餘強類型語言相似,TypeScript遵循ECMAScript 2015標準,支持class類型,同時也增長支持interface類型。數組
下面是一個類的基本定義方式:ide
1 class User { 2 name: string; 3 constructor(_name: string) { 4 this.name = _name; 5 } 6 7 sayHello(): string { 8 return `Hello,${this.name}!`; 9 } 10 } 11 12 let user = new User('John Reese'); 13 user.sayHello();
在上面的例子裏,定義了一個類User,這個類擁有一個屬性、一個構造函數和一個實例方法sayHello。經過new的方式,能夠用這個類實例化一個實例對象,並能夠調用實例方法。這與大多數靜態語言的聲明方式一致。函數
1. 類成員的訪問級別工具
與強類型語言相似,TypeScript的類成員能夠顯式聲明訪問級別:public、protected、privatethis
1 class User { 2 name: string; 3 private sex: string; 4 protected age: number; 5 constructor(_name: string) { 6 this.name = _name; 7 } 8 9 sayHello(): string { 10 return `Hello,${this.name}!`; 11 } 12 } 13 14 let user = new User('John Reese'); 15 user.name = 'Root'; // 公有屬性,能夠賦值 16 user.sex = 'female'; // 私有屬性,沒法賦值 17 user.age = 28; // 受保護屬性,沒法賦值 18 user.sayHello();
在TypeScript裏,若是不顯示指定訪問級別,則默認爲public。spa
2. 屬性的get和set訪問器prototype
1 class User { 2 private _name: string; 3 4 get name(): string { 5 return this._name; 6 } 7 8 set name(newName: string) { 9 this._name = newName; 10 } 11 12 constructor(_name: string) { 13 this.name = _name; 14 } 15 16 sayHello(): string { 17 return `Hello,${this._name}!`; 18 } 19 } 20 21 let user = new User('John Reese'); 22 user.name = 'Root'; 23 user.sayHello();
經過get和set關鍵字聲明屬性訪問器,經過屬性訪問器能夠精確控制屬性的賦值和獲取值。下面是通過編譯後生成的JavaScript代碼code
1 var User = (function () { 2 function User(_name) { 3 this.name = _name; 4 } 5 Object.defineProperty(User.prototype, "name", { 6 get: function () { 7 return this._name; 8 }, 9 set: function (newName) { 10 this._name = newName; 11 }, 12 enumerable: true, 13 configurable: true 14 }); 15 User.prototype.sayHello = function () { 16 return "Hello," + this._name + "! " + this._age; 17 }; 18 return User; 19 }()); 20 var user = new User('John Reese'); 21 user.name = 'Root'; 22 user.sayHello();
3. 靜態屬性對象
靜態屬性便是經過類型而不是實例就能夠訪問的屬性
1 class User { 2 static sex_type = ['male', 'female']; // 靜態屬性 3 name: string; 4 sex: string; 5 6 constructor(_name: string) { 7 this.name = _name; 8 } 9 10 sayHello(): string { 11 return `Hello,${this.name}!`; 12 } 13 } 14 15 let user = new User('John Reese'); 16 user.name = 'Root'; 17 user.sex = User.sex_type[1]; 18 user.sayHello();
經過static關鍵字能夠聲明類型的靜態屬性。
4. 類的繼承
同強類型語言同樣,TypeScript也支持類的繼承
1 // 基類 2 class Animal { 3 name: string; 4 5 constructor(theName: string) { 6 this.name = theName; 7 } 8 9 eat() { 10 console.log(`${this.name} 吃食物。`); 11 } 12 } 13 14 // 子類繼承基類 15 class Dog extends Animal { 16 constructor(theName: string) { 17 super(theName); 18 } 19 20 eat() { 21 super.eat(); 22 console.log('而且吃的是狗糧。'); 23 } 24 } 25 26 class People extends Animal { 27 constructor(theName: string) { 28 super(theName); 29 } 30 31 // 子類重寫基類方法 32 eat() { 33 console.log(`${this.name} 拒絕吃狗糧。`); 34 } 35 } 36 37 let animal = new Animal('動物'); 38 animal.eat(); 39 40 let dog: Animal; 41 dog = new Dog('狗'); 42 dog.eat(); 43 44 let people: Animal; 45 people = new People('人類'); 46 people.eat();
從上面的例子能夠看到,子類經過extends關鍵字能夠繼承其餘類,經過super方法調用基類對應的方法,也能夠直接重寫基類的方法。
下面是編譯以後生成JavaScript源碼,能夠比較看看
1 var __extends = (this && this.__extends) || function (d, b) { 2 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 3 function __() { this.constructor = d; } 4 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 5 }; 6 // 基類 7 var Animal = (function () { 8 function Animal(theName) { 9 this.name = theName; 10 } 11 Animal.prototype.eat = function () { 12 console.log(this.name + " \u5403\u98DF\u7269\u3002"); 13 }; 14 return Animal; 15 }()); 16 // 子類繼承基類 17 var Dog = (function (_super) { 18 __extends(Dog, _super); 19 function Dog(theName) { 20 _super.call(this, theName); 21 } 22 Dog.prototype.eat = function () { 23 _super.prototype.eat.call(this); 24 console.log('而且吃的是狗糧。'); 25 }; 26 return Dog; 27 }(Animal)); 28 var People = (function (_super) { 29 __extends(People, _super); 30 function People(theName) { 31 _super.call(this, theName); 32 } 33 // 子類重寫基類方法 34 People.prototype.eat = function () { 35 console.log(this.name + " \u62D2\u7EDD\u5403\u72D7\u7CAE\u3002"); 36 }; 37 return People; 38 }(Animal)); 39 var animal = new Animal('動物'); 40 animal.eat(); 41 var dog; 42 dog = new Dog('狗'); 43 dog.eat(); 44 var people; 45 people = new People('人類'); 46 people.eat();
5. 抽象類
將上面的例子稍微修改下
1 // 抽象類 2 abstract class Animal { 3 name: string; 4 5 constructor(theName: string) { 6 this.name = theName; 7 } 8 9 abstract eat(); 10 } 11 12 // 子類繼承抽象類 13 class Dog extends Animal { 14 constructor(theName: string) { 15 super(theName); 16 } 17 18 eat() { 19 console.log(`${this.name} 吃狗糧。`); 20 } 21 } 22 23 let animal = new Animal('動物'); // 抽象類沒法實例化 24 animal.eat(); 25 26 let dog: Animal; 27 dog = new Dog('狗'); 28 dog.eat();
經過abstract關鍵字聲明抽象類和抽象方法,子類繼承抽象類後,須要實現抽象方法。一樣的,抽象類不能被實例化。
下面是一個簡單的接口聲明
1 interface Animal { 2 name: string; 3 }
在JavaScript裏沒有對應的類型與之對應,因此編譯以後不會生成任何JavaScript代碼。
1. 做爲參數類型
接口類型能夠做爲方法的參數類型,效果等同於直接指定Json對象的類型。
1 interface Animal { 2 name: string; 3 } 4 5 let printName = function(param: Animal) { 6 console.log(`Name is ${param.name}`); 7 } 8 9 printName({name: 'Dog'});
一樣,接口成員也能夠是缺省的
1 interface Animal { 2 name: string; 3 age?: number; 4 } 5 6 let printName = function (param: Animal) { 7 if (param.age) { 8 console.log(`Name is ${param.name}, and age is ${param.age}`); 9 } else { 10 console.log(`Name is ${param.name}`); 11 } 12 } 13 14 printName({ name: 'Dog' }); 15 printName({ name: 'Dog', age: 5 });
可是在某些狀況下,調用方法時,參數賦值可能會有多個,接口在做爲參數類型時也支持擁有多個成員的狀況。
1 interface Animal { 2 name: string; 3 age?: number; 4 [propName: string]: any; // 其餘成員 5 } 6 7 let printName = function (param: Animal) { 8 if (param.age) { 9 console.log(`Name is ${param.name}, and age is ${param.age}`); 10 } else { 11 console.log(`Name is ${param.name}`); 12 } 13 } 14 15 printName({ name: 'Dog' }); 16 printName({ name: 'Dog', age: 5 }); 17 printName({ name: 'Dog', age: 5, character: '粘人' }); // 多於明肯定義的屬性個數
2. 做爲其餘類型
接口也能夠定義方法的類型,和數組類型
1 interface FuncType { 2 (x: string, y: string): string; // 聲明方法成員 3 } 4 5 let func1: FuncType; 6 func1 = function (prop1: string, prop2: string): string { // 方法參數名稱不須要與接口成員的參數名稱保持一致 7 return prop1 + ' ' + prop2; 8 } 9 10 interface ArrayType { 11 [index: number]: string; // 聲明數組成員 12 } 13 14 let arr: ArrayType; 15 arr = ['Dog', 'Cat'];
3. 接口的繼承與實現
同強類型語言同樣,TypeScript的接口支持繼承與實現。
1 interface Animal { 2 name: string; 3 eat(): void; 4 } 5 6 class Dog implements Animal { 7 name: string; 8 constructor(theName: string) { 9 this.name = theName; 10 } 11 12 eat() { 13 console.log(`${this.name} 吃狗糧。`) 14 } 15 } 16 17 class Cat implements Animal { 18 name: string; 19 constructor(theName: string) { 20 this.name = theName; 21 } 22 23 eat() { 24 console.log(`${this.name} 吃貓糧。`) 25 } 26 } 27 28 let dog: Animal; 29 dog = new Dog('狗狗'); 30 dog.eat(); 31 32 let cat: Animal; 33 cat = new Cat('喵星人'); 34 cat.eat();
類經過implements關鍵字繼承接口,並實現接口成員。
同時,接口也能夠多重繼承。
1 interface Animal { 2 name: string; 3 eat(): void; 4 } 5 6 interface Person extends Animal { // 繼承自Animal接口 7 use(): void; 8 } 9 10 class People implements Person { 11 name: string; 12 constructor(theName: string) { 13 this.name = theName; 14 } 15 16 eat() { 17 console.log(`${this.name} 拒絕吃狗糧。`) 18 } 19 20 use() { 21 console.log(`${this.name} 會使用工具。`) 22 } 23 } 24 25 let man: Person; 26 man = new People('男人'); 27 man.eat(); 28 man.use();
4. 類型轉換
在TypeScript裏,接口能夠對符合任一成員類型的對象進行轉換,轉換以後的對象自動繼承了接口的其餘成員。
1 interface Animal { 2 name: string; 3 age: number; 4 eat(): void; 5 } 6 7 let thing = { name: '桌子' }; 8 let otherThing = <Animal>thing; // 類型轉換 9 otherThing.age = 5; 10 otherThing.eat = function () { 11 console.log(`${this.name} 不知道吃什麼。`) 12 };
上面的例子裏,聲明瞭擁有name屬性的json對象,經過<>將json對象轉換成了Animal類型的對象。轉換後的對象則擁有了另外的age屬性和eat方法。
5. 接口繼承類
在TypeScript裏,接口能夠繼承類,這樣接口就具備了類裏的全部成員,同時這個接口只能引用這個類或者它的子類的實例。
1 class People { 2 name: string; 3 private age: number; 4 constructor(theName: string) { 5 this.name = theName; 6 } 7 8 eat() { 9 console.log(`${this.name} 拒絕吃狗糧。`); 10 } 11 12 use() { 13 console.log(`${this.name} 會使用工具。`) 14 } 15 } 16 17 interface Animal extends People { // 接口 18 19 } 20 21 class Man extends People { // 子類 22 23 } 24 25 class Cat { // 擁有一樣結構的另一個類 26 name: string; 27 private age: number; 28 constructor(theName: string) { 29 this.name = theName; 30 } 31 32 eat() { 33 // 具體實現 34 } 35 36 use() { 37 // 具體實現 38 } 39 } 40 41 let cat: Animal; 42 cat = new Cat('喵星人'); // Cat類不是People的子類,沒法被Animal引用 43 44 let man: Animal; 45 man = new Man('男人'); 46 man.eat();
當繼承鏈過深,代碼須要在某一個子類的類型下執行時,這種方法比較有效。