前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【愚公系列】2021年12月 Typescript-接口的使用

【愚公系列】2021年12月 Typescript-接口的使用

作者头像
愚公搬代码
发布2022-12-01 09:18:20
4940
发布2022-12-01 09:18:20
举报
文章被收录于专栏:历史专栏

文章目录


一、接口概念

接口的作用:在面向对象OOP的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。

接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。 typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。

OOP : Object Oriented Programming

定义标准。

  • 属性类接口
  • 函数类型接口
  • 可索引接口
  • 类类型接口
  • 接口扩展
代码语言:javascript
复制
  1. 类实现接口
  2. 接口可以继承接口
  3. 类可以继承类, 类也可以实现接口
  4. 接口继承类
  5. 总结
  • 接口与type的区分

二. 属性类接口

1.1 未使用接口的情况:

print的参数是一个对象, 但是必须有label这个属性 ts中自定义方法传入参数,对json进行约束

代码语言:javascript
复制
function print(labelObj:{ label:string }){
    console.log(  labelObj.label );
}

// 跳过这些检查的方式, 它就是将这个对象赋值给一个另一个变量: 
// myObj 不会经过额外属性检查,所以编译器不会报错。
print({name:'张三'});  //错误的写法
print({label:'你好'});  //正确的写法

print({
        label:"Size test",
        name:'sss'
} as { label:string });

注意下面写法会提示报错:

代码语言:javascript
复制
print({
      label:"Size test",
      name:'laney'
});

将对象字面量作为参数传递的时候, 对象字面量会被特殊对待而且会经过"额外属性检查"。 如果一个对象字面量存在任何"目标类型"不包含的属性时,你会得到一个错误。

如何跳过这种检查

官网上给了三种方式:

  • 第一种使用类型断言:
代码语言:javascript
复制
print({
        label:"Size test",
        name:'laney'
} as { label:string });
  • 第二种添加字符串索引签名:
代码语言:javascript
复制
function print(labelObj:{ label:string ,[propName: string]: any;}){
        console.log(labelObj);
 }
print({
        label:"Size test",
        name:'laney'
});
  • 第三种将字面量赋值给另外一个变量:

它就是将这个对象赋值给一个另一个变量: myObj 不会经过额外属性检查,所以编译器不会报错。

let myObj={ size:10,label:“Size 10 Object” }; print(myObj);

这是一个简单的函数,在调用print时,会检查传入参数的类型,并且只检查那些必需属性是否存在,并且类型是否匹配。下面利用接口重写上面的例子

1.2 使用接口

代码语言:javascript
复制
//接口
interface labelValue{
    label:string;
    size:number;
}
//type 别名
type labelObj = {
    label?:string;
    size:number;
}
 function print(labelObj:labelValue){
    console.log( 'labelObj.label' );
    console.log( labelObj );
 }
 let myObj2={size:10,label:"Size 10 Object",name:'Alice' };
 print(myObj2);

在参数类型检查的时候,会发现参数遵循的是接口labelValue的规范,然后就回去检查是不是符合接口所描述的规范。 接口就相当于是一个约束,让大家都遵循。

//接口:行为和动作的规范,对批量方法进行约束 //就是传入对象的约束 属性接口

1.3 类型断言

类型断言 Type Assertion

定义: 可以用来手动指定一个值的类型 语法

  • 方式一: <类型> 值
代码语言:javascript
复制
function getLength(x:number|string):number{
    if((<string>x).length) {
        return (<string>x).length
    } else {
        return x.toString().length
    }
}
  • 方式二: 值 as 类型
代码语言:javascript
复制
function getLength(x:number|string):number{
    if((x as string).length){
        return (x as string).length
    } else {
        return x.toString().length
    }
}

类型断言并非是类型转换,断言一个联合类型中不存在的类型会报错!

代码语言:javascript
复制
function wrong(x:number|string):boolean{
    return <boolean>x    //  报错!
}

1.3 额外的属性检测

代码语言:javascript
复制
interface labelValue{
    label:string;
    size:number;
}
 function print(labelObj:labelValue){
    console.log( 'labelObj.label' );
    console.log( labelObj );
 }

上面1.2 使用接口 的例子,如果这么这么传值会报错 print({ label:‘hello’, size:30, name:‘laney’ });

如何绕过额外属性检查,官方给了3种方式

解决方式:

代码语言:javascript
复制
//  第一种使用类型断言
print({
    label1:'hello',
    size:30
} as labelValue);

// 第二种添加字符串索引签名
/* interface labelValue{
    label?:string;
    size:number;
    [propName: string]: any;    //+
} */

//将字面量赋值给另外一个变量
    var labK = {
      label1:'hello',
      size:30
  };
print(labK);

1.4 可选属性

代码语言:javascript
复制
interface FullName{
    firstName:string;   //注意;结束
    secondName:string;
    // secondName?:string; //可选属性,可传可不传
    hello(str:string):string
}
function printName(name:FullName){
    // 必须传入对象  firstName  secondName
    console.log(name.firstName+'--'+name.secondName);
    console.log(name)
}
// printName('1213');  //错误
/*传入的参数必须包含 firstName  secondName*/
var obj={   
    firstName:'张',
    secondName:'三',
    hello(str:string):string{
        return obj.firstName+obj.secondName;
    }
};
printName(obj)


//如果secondName是可选参数,传参的时候可加以不加
// printName({
//     firstName:'firstName'
// })

通过ajax实例演示 属性类接口

代码语言:javascript
复制
/*
       $.ajax({
             type: "GET",
             url: "test.json",
             data: {username:$("#username").val(), content:$("#content").val()},
             dataType: "json"             
         });
         
*/

interface Config{
    type:string;
    url:string;
    data?:string;
    dataType:string;
}

//原生js封装的ajax 
function ajax(config:Config){
   var 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('chengong');
            if(config.dataType=='json'){
                console.log(JSON.parse(xhr.responseText));
            }else{
                console.log(xhr.responseText)
            }
        }
   }
}
ajax({
    type:'get',
    data:'name=zhangsan',
    url:'http://localhost:3000/test', //api
    dataType:'json'
})

三、函数类型接口

ts中函数型接口,非常类似于java、c#中使用lambda表达式传入匿名函数。 因为对象中仅包含一个函数,这个对象的全部意义也仅在于那个可被外部调用的函数,故而称之为函数型接口。

加密的函数类型接口

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

代码语言:javascript
复制
interface encrypt{
    (key:string,value:string):string;
}

var md5:encrypt=function(key:string,value:string):string{
        //模拟操作
        return key+value;
}
console.log(md5('name','zhangsan'));

var sha1:encrypt=function(key:string,value:string):string{
    //模拟操作
    return key+'----'+value;
}

console.log(sha1('name','lisi'));
代码语言:javascript
复制
//对两个数进行运算得到另一个数  函数型接口
interface  CalcTwo{
    (a:number,b:number):number;
}

/**
 * 计算数组被某种算法运算的结果
 * @param {number[]} arr  数组
 * @param {CalcTwo} calc  用户指定的算法函数
 * @param {number} initVal  传入初始值
 * @returns {number}  得到最终运算结果
**/

function calcArr(arr:number[],calc:CalcTwo,initVal:number):number{
      var result=initVal;
      arr.forEach((value)=>{
          result = calc(result,value);
      });
     return result;
}

使用:

代码语言:javascript
复制
 var arr:number[]=[1,3,5,7,9];
 var  add=function(a:number,b:number):number{
      return a+b;
  };
 
 var  multiply=function(a:number,b:number):number{
     return a*b;
 };
console.log("相加",calcArr(arr, add, 0));//25
console.log("相乘",calcArr(arr, multiply, 1));//945



//或者直接传入一个匿名函数亦可
var s1=calcArr(arr,function(a,b){
    return a*b;
},1);
console.log("s1",s1);//945
 
 var s2=calcArr(arr,function (a,b) {
     return (a+1)*(b+1);
 },1);
console.log("s2",s2);//10170

四、可索引接口

(不常用)

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

ts定义数组的方式 /* var arr:number[]=[2342,235325] var arr1:Array=[‘111’,‘222’] */

4.1 可索引接口 对数组的约束

代码语言:javascript
复制
 interface UserArr{
     [index:number]:string
 }

 var arr:UserArr=['aaa','bbb'];
 console.log(arr[0]);
 var arr:UserArr=[123,'bbb'];  /*错误*/
 console.log(arr[0]);

4.2 可索引接口 对对象的约束

代码语言:javascript
复制
 interface UserObj{
   [index:string]:string
 }

 var arr:UserObj={name:'张三'};

五、类类型接口 ,利用implements实现接口

类类型接口用来规范一个类的内容。示例代码如下

用的多,和抽象类有点相似, 类实现接口本质上 即类遵循接口的约束,接口里面写了多少个函数、参数,实现的类里面也要写相同的函数、参数。

代码语言:javascript
复制
interface FullName {  
  username: string;  
}  
//Person这个类需要遵循接口FullName的约束
class Person implements FullName {  
  username: string;  
  constructor(n: string) { 
      this.username = n;
  }  
}  

可以在接口中描述一个方法,并在类里具体实现它的功能,如同下面的setName方法一样:

代码语言:javascript
复制
interface FullName {  
  username: string;  
  setName(name: string): void;  
  getName():void;
}  
class Person implements FullName {  
  username: string;    
  constructor(name: string) {
      this.username = name;
   } 
   setName(str: string) {  
      this.username = str;
   } 
  getName(){
    console.log(this.username)
  } 
} 

var p1 = new Person('Alice');
p1.setName('tony');
p1.getName(); 

下面这个不演示,自行学习

代码语言:javascript
复制
interface Animal{
        name:string;
        eat(str:string):void;
    }

class Dog implements Animal{
    name:string;
    constructor(name:string){
        this.name=name;
    }
    eat(){
        console.log(this.name+'吃粮食')
    }
}

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

class Cat implements Animal{
    name:string;
    constructor(name:string){
        this.name=name;
    }
    eat(food:string){
        console.log(this.name+'吃'+food);
    }
}

var c=new Cat('小花');
c.eat('老鼠');

六、接口扩展

6.1 类实现接口 - 类类型接口 前面已讲

代码语言:javascript
复制
interface ClockInterface{
  currentTime:Date;
  getTime(d:Date):any;
}
class Clock implements ClockInterface{
  currentTime:Date;
  constructor(){
      this.currentTime = new Date()
  }
  getTime(){
      console.log("123");
      console.log(this.currentTime)
  }
  
}
let clock1=new Clock();
clock1.getTime();

类实现接口本质上也是一样的,即类遵循接口的约束,接口里面写了多少个函数、参数,实现的类里面也要写相同的函数、参数。

接口继承就是说接口可以通过其他接口来扩展自己。

Typescript 允许接口继承多个接口。 继承使用关键字 extends。

6.2 接口可以继承接口

代码语言:javascript
复制
interface Animal {
  eat(): void;
}
// Person 接口继承了 接口Animal
// 单接口继承,只继承一个接口
interface Person extends Animal {
  work(): void;
}
//类实现接口  WebFront类实现接口Person
class WebFront implements Person {
  public name: string;
  constructor(name: string) {
    this.name = name;
  }

  eat() {
    console.log(this.name + "喜欢吃馒头");
  }
  work() {
    console.log(this.name + "写代码");
  }
}
var w = new WebFront("小李");
w.eat();

接口的扩展就是给多添加了一些约束。一个接口可以扩展多个接口,当一个接口扩展另一个接口,也继承了该接口的约束。

6.3. 类可以继承类, 同时类也可以实现接口

代码语言:javascript
复制
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);
  }
}

//类继承类并实现接口:  WebFront继承类Programmer 并实现接口Person
class WebFront extends Programmer implements Person {
  constructor(name: string) {
    super(name);
  }
  eat() {
    console.log(this.name + "喜欢吃馒头");
  }
  work() {
    console.log(this.name + "写代码");
  }
}
var w = new WebFront("小李");
w.eat();
w.coding("写ts代码");

6.4. 接口继承类

代码语言:javascript
复制
class Point{
    x:number;
    y:number;
    constructor(x:number,y:number){
      this.x = x;
      this.y = y;

        }
        log(){
          console.log('123456');
        }
    }

 interface Point3d extends Point{
        z:number;
 }
 
var point3d:Point3d={
        x:1,
        y:2,
        z:3,
        log(){
            console.log('7777');
        }
    }
point3d.log();//7777

官方解释:当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 也就是说,接口继承类值继承了它的约束条件,具体的值并不继承。

总结

接口扩展(继承)接口 interfaceA extends interfaceB

类实现接口 classA implements interfaceA

接口继承类 interfaceB extends classB

类扩展(继承)类 classA extends classB

有人肯分不清上面时候用extends,什么时候用implements。

记住一句话,只要涉及到继承就是extends.

interface 与 type 声明类型的区别

代码语言:javascript
复制
namespace InterfaceAndType {
    //  1. Objects / Functions
    // 两者都可以用来描述对象或函数的类型,但是语法不同。

    // Interface
    // interface Point {
    //     x: number;
    //     y: number;
    //   }
      
    //   interface SetPoint {
    //     (x: number, y: number): void;
    //   }


    //   Type alias

    type Point = {
        x: number;
        y: number;
      };
      
    type SetPoint = (x: number, y: number) => void;







    // 2. Other Types 类型别名还可以用于其他类型,如基本类型(原始值)、联合类型、元组。Interface不行
       // primitive 基本类型
        type Name = string;

        // object
        type PartialPointX = { x: number; };
        type PartialPointY = { y: number; };

        // union
        type PartialPoint = PartialPointX | PartialPointY;

        // tuple
        type Data = [number, string];

        // dom
        let div = document.createElement('div');
        type B = typeof div;


        // 3. Extend
        // 两者都可以扩展,但是语法又有所不同。此外,请注意接口和类型别名不是互斥的。接口可以扩展类型别名,反之亦然。

        
        // Interface extends interface

        interface PointX { x: number; }
        interface PointY extends PointX { y: number; }

        // Type alias extends type alias

        type PointA = { x: number; };
        type PointB = PointA & { y: number; };

        // Interface extends type alias

        type SizeA = { x: number; };
        interface SizeB extends SizeA { y: number; }

        // Type alias extends interface

        interface SizeX { x: number; }
        type SizeY = SizeX & { y: number; };



    //    4. class Implements

    // 类可以以相同的方式实现接口或类型别名。但是请注意,类和接口被认为是静态的。因此,它们不能实现/扩展命名联合类型的类型别名。
    interface PointV {
        x: number;
        y: number;
      }
      
      class SomePoint implements PointV {
          x:number;
          y:number;
          constructor(x:number,y:number){
                this.x = x;
                this.y = y;
          }
      }
      
      type PointV2 = {
        x: number;
        y: number;
      };
      
      class SomePoint2 implements PointV2 {
        x:number;
        y:number;
        constructor(x:number,y:number){
              this.x = x;
              this.y = y;
        }
      }
      
      type PointXX = { x: number; } | { y: number; };
      
      // FIXME: can not implement a union type
    //   class SomePoint3 implements PointXX {
    //         x:number;
    //         y:number;
    //         constructor(x:number,y:number){
    //             this.x = x;
    //             this.y = y;
    //         }
    //   }

}

总结

interface 和 type 很像,很多场景,两者都能使用。但也有细微的差别:

类型:对象、函数两者都适用,但是 type 可以用于基础类型、联合类型、元祖。 同名合并:interface 支持,type 不支持。 计算属性:type 支持, interface 不支持。 总的来说,公共的用 interface 实现,不能用 interface 实现的再用 type 实现。主要是一个项目最好保持一致。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、接口概念
  • 二. 属性类接口
    • 1.1 未使用接口的情况:
      • 1.2 使用接口
        • 1.3 类型断言
          • 1.3 额外的属性检测
            • 1.4 可选属性
              • 通过ajax实例演示 属性类接口
          • 三、函数类型接口
          • 四、可索引接口
            • 4.1 可索引接口 对数组的约束
              • 4.2 可索引接口 对对象的约束
              • 五、类类型接口 ,利用implements实现接口
              • 六、接口扩展
                • 6.1 类实现接口 - 类类型接口 前面已讲
                  • 6.2 接口可以继承接口
                    • 6.3. 类可以继承类, 同时类也可以实现接口
                      • 6.4. 接口继承类
                        • interface 与 type 声明类型的区别
                        • 总结
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档