TypeScript抽象類、約束和接口、接口擴展接口繼承

抽象類

抽象類做爲其它派生類的基類使用。 它們一般不會直接被實例化。 不同於接口,抽象類可以包含成員的實現細節。 abstract關鍵字是用於定義抽象類和在抽象類內部定義抽象方法。

abstract class Animal {
    abstract makeSound(): void;
    move(): void {
        console.log('roaming the earch...');
    }
}

抽象類中的抽象方法不包含具體實現並且必須在派生類中實現。 抽象方法的語法與接口方法相似。 兩者都是定義方法簽名但不包含方法體。 然而,抽象方法必須包含 abstract關鍵字並且可以包含訪問修飾符。

abstract class Department {

    constructor(public name: string) {
    }

    printName(): void {
        console.log('Department name: ' + this.name);
    }

    abstract printMeeting(): void; // 必須在派生類中實現
}

class AccountingDepartment extends Department {

    constructor() {
        super('Accounting and Auditing'); // 在派生類的構造函數中必須調用 super()
    }

    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }

    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}

let department: Department; // 允許創建一個對抽象類型的引用
department = new Department(); // 錯誤: 不能創建一個抽象類的實例
department = new AccountingDepartment(); // 允許對一個抽象子類進行實例化和賦值
department.printName();
department.printMeeting();
department.generateReports(); // 錯誤: 方法在聲明的抽象類中不存在

 abstract 抽象方法只能放在抽象類裏面。

用abstract 關鍵字定義抽象類 和方法,抽象類和抽象方法用來定義標準,抽象類中的抽象類派生類必須實現,抽象類中的非抽象方法,派生類可以不實現。

接口

TypeScript的核心原則之一是對值所具有的結構進行類型檢查。 它有時被稱做“鴨式辨型法”或“結構性子類型化”。 在TypeScript裏,接口的作用就是爲這些類型命名和爲你的代碼或第三方代碼定義契約,它定義了行爲和動作的規範,在程序設計裏面接口起到一種,規範或者限制作用,接口定義了一了某一批類所需要遵守的規範,接口不關心這些類的內部狀態數據,也不關心這些類裏面方法的實現細節,它只規定這批類中必須提供某些方法,提供這些方法的類就可以滿足實際需要。typeScript 中的接口類似於java,同時還增加了更靈活的接口類型,包括屬性、方法、可索引和類。

約束


function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);


 

類型檢查器會查看printLabel的調用。 printLabel有一個參數,並要求這個對象參數有一個名爲label類型爲string的屬性。 需要注意的是,我們傳入的對象參數實際上會包含很多屬性,但是編譯器只會檢查那些必需的屬性是否存在,並且其類型是否匹配。 然而,有些時候TypeScript卻並不會這麼寬鬆,我們下面會稍做講解。

屬性接口

下面我們重寫上面的例子,這次使用接口來描述:必須包含一個label屬性且類型爲string

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

LabelledValue接口就好比一個名字,用來描述上面例子裏的要求。 它代表了有一個 label屬性且類型爲string的對象。 需要注意的是,我們在這裏並不能像在其它語言裏一樣,說傳給 printLabel的對象實現了這個接口。我們只會去關注值的外形。 只要傳入的對象滿足上面提到的必要條件,那麼它就是被允許的。

還有一點值得提的是,類型檢查器不會去檢查屬性的順序,只要相應的屬性存在並且類型也是對的就可以。

interface FullName{
    firstName:string;
    secondName:string;
}

function printName(obj:FullName){
    //必須傳入對象 firstName  secondName
    console.log(obj.fristName +"----"+obj.secodName);
}

//printName('1213'); //報錯 因爲兩個屬性都要傳
var obj = {
    age:22,
    firstName: '張',
    secondName:'三豐'
}
printName(obj)// 正確,因爲obj 中兩個屬性都有

可選屬性接口

interface FullName{
    firstName:string;
    secondName?:string;
}

functin getName(obj:FullNmae){
    console.log(obj.firstName+"------"+secondName);
}

getName({
    firstName:'張'  //此時secondName 可傳,也可不傳
})

函數類型接口

對方法傳入的參數以及返回值進行約束,可以用來做加密的函數類型接口

interface encrypt{
    (key:string,value:string):string;
}
var md5:encrypt = function(key:string,value:string):string{
    //模擬md5加密
    return key+value;
}
console.log(md5('name','zsf'));

var rsa:encrypt = function(key:string,value:string):string{
    //模擬RSA非對稱加密
    return key+"/\/\/\"+value;
}
console.log(rsa('name','zsf'));

可索引類型接口

對數組或者對象的約束

對數組的約束:

/*  ts 定義數組的方式
 *  var arr:number[] = [233,123];
 *  var arr1:string[] =["xx",'yy'];
 */
interface UserArr{
    [index:number]:string  //index表示索引值,規定索引值爲number ,value 是個string
}
 
var arr:UserArr = ['x','y'];
console.log(arr[0]+"---"arr[1]);

 對對象的約束

 
interface UserObj{
    [index:string]:string  //index表示屬性類型,規定屬性類型爲爲string ,value 是屬性值爲string
}
 
var  obj = UserObj= {name:'zsf'};

類類型接口

對類的約束和抽象類有點相似( 可以發現類類型接口就是將方法類型接口和屬性類型接口結合起來)

注意:類類型接口中的屬性和方法,在它的實現類中都必須要有,屬性和方法都要在實現類中體現

interface Animal{
    //可以發現類類型接口就是將方法類型接口和屬性類型接口結合起來
    
    name:string;  //屬性類型接口

    eat(str:string):void;  //方法類型接口
}

//實現接口
var Dog implements Animal{
    name:string;
    constructor(name:string){
      this.name = name;   
    }
    
   eat(){
       console.log(this.name+'eat')
    }
}

var d  = new Dog("小黑");
d.eat();

只讀屬性接口

一些對象屬性只能在對象剛剛創建的時候修改其值。 你可以在屬性名前用 readonly來指定只讀屬性:

interface Point {
    readonly x: number;
    readonly y: number;
}

你可以通過賦值一個對象字面量來構造一個Point。 賦值後, xy再也不能被改變了。

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

TypeScript具有ReadonlyArray<T>類型,它與Array<T>相似,只是把所有可變方法去掉了,因此可以確保數組創建後再也不能被修改:

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

上面代碼的最後一行,可以看到就算把整個ReadonlyArray賦值到一個普通數組也是不可以的。 但是你可以用類型斷言重寫:

a = ro as number[];

readonly vs const

最簡單判斷該用readonly還是const的方法是看要把它做爲變量使用還是做爲一個屬性。 做爲變量使用的話用 const,若做爲屬性則使用readonly

接口擴展

interface Animal{
    eat():void;
}

interface Person extends Animal{
    work():void;
}

class Programmer{
  public name:string;
    constructor(name:string){
     this.name = name;
    }
    coding(code:string){
            console.log(this.name +"===寫了===="+code);
    }
}

class Worker extends Programmer implements Person(){
    constructor(name:string){
        super(name);
    }
    
    eat(){
        console.log(this.name +"----"+eat)
    }
     work(){
        console.log(this.name +"----"+work)
    }
}
 var  w = new Wroker('張三丰');
 w.work();
 w.eat();
 w.coding("HELLO WORLD");

 類不僅可以繼承,也可以實現接口,繼承了幾個接口,那麼就要級聯實現接口中的所有方法。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章