TypeScript抽象类、约束和接口、接口扩展接口继承

本文详细介绍了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");

 类不仅可以继承,也可以实现接口,继承了几个接口,那么就要级联实现接口中的所有方法。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值