TypeScript

  • 属性接口
  • 函数类型接口
  • 可索引接口
  • 类类型接口
  • 接口扩展

在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范。在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范,它不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。Typescript中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。

属性接口

首先,如果我们想自定义方法来实现对json数据的约束:

function printLabel(labelInfo:{label:string}):void{
    console.log("printLabel");
}
// 现在还不满足重用性
function printLabel2(labelInfo:{label:string}):void{
    console.log("printLabel2");
}
// printLabel("李四");   // 错误写法
// printLabel({name:"李四"}); // 错误写法
printLabel({label:"李四"}); // 正确写法

这么操作很不方便。如果对象中存储很多数据,这么做约束太不灵活了。并且,现在也不满足重用性,如果其它函数也想用它,就得再次声明。

接口就可以帮我们完成这项工作。

接口就是行为和动作的规范,并能对批量方法传入的参数进行约束。接口的关键字:interface

// 传入对象的约束:属性接口
interface FullName {
    // 约束我们一定要传入以下参数,并且注意数据类型
    firstName: string
    secondName: string
}
function printName(name:FullName):void{
    console.log(name.firstName + name.secondName);
}
printName({
    firstName: "李",
    secondName: "四"
})

Webstorm还很贴心的为我们做了标记:

typescript的参数为函数怎么写 typescript 函数类型_接口


那么如果我们不按照约束可以吗:

// 传入对象的约束:属性接口
interface FullName {
    // 约束我们一定要传入以下参数,并且注意数据类型
    firstName: string
    secondName: string
}
function printName(name:FullName):void{
    console.log(name.firstName + name.secondName);
}
printName({
    firstName: "李",
    secondName: "四",
    age: 20
})

此时我们可以发现,ts会报错说,接口中不存在这样的一个参数:

typescript的参数为函数怎么写 typescript 函数类型_typescript_02


但是!页面是肯定可以输出age的内容的。看一下js中的内容就明白了。

typescript的参数为函数怎么写 typescript 函数类型_typescript的参数为函数怎么写_03


虽然在ts中,这么传递参数会报错,但其实有一种可以越过检测的方式:

// 传入对象的约束:属性接口
interface FullName {
    // 约束我们一定要传入以下参数,并且注意数据类型
    firstName: string
    secondName: string
}
function printName(name:FullName):void{
    console.log(name.firstName + name.secondName);
    // 虽然不报错,但是在ts中使用age,依然会报错
    console.log(name.age);
}
let obj = {
    age: 20,
    firstName: "李",
    secondName: "四"
}
// 这么传参,虽然age不存在接口中,但是不会报错
printName(obj)

此时,我们可以发现,在传递参数时已经不会报错了,但是,如果我们想使用age,那肯定会报错了,因为在接口中不存在。

typescript的参数为函数怎么写 typescript 函数类型_接口_04


同样的,在页面中肯定能输出age的数据。

那如果我们现在想传递这个age参数,一种方法就是在接口中直接定义了。那如果有的函数想用age,有的函数不想用age,该怎么做?

我们也可以使用可选参数:

interface FullName {
    firstName: string
    secondName: string
    age?:number
}
function printName(name:FullName):void{
    console.log(name.firstName + name.secondName);
}
function printAge(info:FullName):void{
    console.log(info.firstName + info.secondName + info.age);
}
printName({
    firstName: "李",
    secondName: "四"
})
// 传递参数没有顺序
printAge({
    age: 20,
    secondName: "四",
    firstName: "李"
})

这样无论传参不传参age都不会报错了。

除此以外,还有一种定义参数的方式:

interface FullName {
    firstName: string
    secondName: string
    age?:number
    // 参数名是string类型,可以给它一个any类型数据
    [propname:string]:any
}
function printName(name:FullName):void{
    console.log(name.sex);
}
printName({
    firstName: "李",
    secondName: "四",
    sex: "男"
})

此时,我们就可以传入一个比较随意的参数了。

最后还是要说,虽然我们可以不管ts报错信息,不管接口规范,它在页面中也能展示信息,但既然选择使用ts,那显然这些规范是必须遵守的。

实例:

interface Config {
    type: string;
    url: string;
    data?:string;
    dataType: string;
}
// 原生js封装ajax,不支持ie6
function ajax(config:Config){
    let xhr = new XMLHttpRequest();
    xhr.open(config.type,config.url, true);
    xhr.send(config.data);
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4 && xhr.status == 200){
            console.log('成功');
            if(config.dataType == 'json'){
                JSON.parse(xhr.responseText);
            }else{
                console.log(xhr.responseText)
            }
        }
    }
}
ajax({
    type: 'get',
    url: '', //填写相关api接口
    dataType: 'json',
    data: 'name=zhangsan'
})

函数类型接口

函数类型接口:对方法传入的参数以及返回值进行约束。

interface FullName {
    say():string
}
function printName(name:FullName):void{
    console.log(name.say())
}
printName({
    say(){
        return "我是李四"
    }
})
// 匿名函数类型接口
interface encrypt {
    (key:string,value:string):string;
}
// 一定要遵守接口约束
let md5:encrypt = function(key:string,value:string):string{
    // 模拟操作
    return key+value;
}
console.log(md5("name","李四"))

对于属性接口,毕竟可以对批量传参进行约束,看起来还是有用的。那看到函数类型接口,确实会有人觉得很没用,因为函数本身就可以对参数和返回值进行约束。之前已经说过,接口也是一种标准,确实函数本身也能对这些内容进行限制,但是如果接触到了项目就会明白,有一个相关说明和规范是多么重要。并且,如果接口中设计的内容再多一些,那为了满足重用性,使用这种方式也是必然。

(当然,一般情况下,公司项目组会提供相关接口文档api)

现在已经接触到了三种规范:接口、多态、抽象类。

可索引接口

可索引接口:对数组、对象的约束(不常用)。

// 可索引接口:对数组的约束
interface UserArr{
    [index:number]:string;
}
let arr:UserArr = ['a','b','c'];
console.log(arr[0]);
// 可索引接口:对对象的约束
interface UserObj{
    [index:string]:string;
}
let arr1:UserObj = {name: '李四'};
console.log(arr1.name);

类类型接口

类类型接口:对类的约束,和抽象类很相似。

interface Animal{
    name:string;
    eat(str:string):void;
}
// 实现接口用关键词implements
class Dog implements Animal{
    name:string;
    constructor(name:string) {
        this.name = name;
    }
    // 必须实现方法,但是参数和数据类型不必相同
    eat():string{
        console.log(this.name + '吃肉')
        return this.name;
    }
}
let d = new Dog('旺财');
console.log(d.eat());

接口扩展

接口扩展:接口可以继承接口。

interface Animal{
    eat():void;
}
interface Person extends Animal{
    work():void;
}
// 实现接口用关键词implements
class Web implements Person{
    name:string;
    constructor(name:string) {
        this.name = name;
    }
    // 不仅要实现相应接口中的属性和方法,
    // 还要实现接口继承的接口中的属性和方法,
    // 否则报错
    eat():void{
        console.log(this.name + '吃饭');
    }
    work():void{
        console.log(this.name + '工作');
    }
}
let my = new Web('李四');
my.eat();
my.work();

类可以在实现接口的同时继承父类。

interface Animal{
    eat():void;
}
interface Person extends Animal{
    work():void;
}
class Programmer{
    name:string;
    constructor(name:string) {
        this.name = name;
    }
    coding(code:string){
        console.log(this.name + code)
    }
}
class Web extends Programmer implements Person{
    constructor(name:string) {
        super(name)
    }
    eat():void{
        console.log(this.name + '吃饭');
    }
    work():void{
        console.log(this.name + '工作');
    }
}
let my = new Web('李四')
my.coding('写ts代码')

到此为止,已经可以看出,接口和抽象类的区别:
1、抽象类需要用abstract关键词,接口需要用interface关键词;
2、抽象类和接口都是其它类的定义规范,不能直接被实例化;
3、类想使用抽象类,需要用extends继承抽象类。类想使用接口,需要用implements实现接口。除此以外,接口也可以使用接口,需要用extends继承接口。
4、抽象方法只能存放在抽象类中,它不包含具体实现并且必须在派生类中实现,而且严格遵守传入参数数量和数据类型。抽象类中也可以有非抽象方法,在继承抽象类的子类中,不需要一定去实现非抽象方法。而接口中的所有属性和方法都必须实现,但参数和数据类型不用严格对照。