④TypeScript 接口(属性接口、函数类型接口、可索引接口、类类型接口、接口扩展)

小鱼儿 2022-12-19 13:26 602阅读 0赞

TypeScript

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

写下博客主要用来分享知识内容,并便于自我复习和总结。
如有错误之处,请各位大佬指出。


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


属性接口

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

  1. function printLabel(labelInfo:{ label:string}):void{
  2. console.log("printLabel");
  3. }
  4. // 现在还不满足重用性
  5. function printLabel2(labelInfo:{ label:string}):void{
  6. console.log("printLabel2");
  7. }
  8. // printLabel("李四"); // 错误写法
  9. // printLabel({name:"李四"}); // 错误写法
  10. printLabel({ label:"李四"}); // 正确写法

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

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

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

  1. // 传入对象的约束:属性接口
  2. interface FullName {
  3. // 约束我们一定要传入以下参数,并且注意数据类型
  4. firstName: string
  5. secondName: string
  6. }
  7. function printName(name:FullName):void{
  8. console.log(name.firstName + name.secondName);
  9. }
  10. printName({
  11. firstName: "李",
  12. secondName: "四"
  13. })

Webstorm还很贴心的为我们做了标记:
在这里插入图片描述
那么如果我们不按照约束可以吗:

  1. // 传入对象的约束:属性接口
  2. interface FullName {
  3. // 约束我们一定要传入以下参数,并且注意数据类型
  4. firstName: string
  5. secondName: string
  6. }
  7. function printName(name:FullName):void{
  8. console.log(name.firstName + name.secondName);
  9. }
  10. printName({
  11. firstName: "李",
  12. secondName: "四",
  13. age: 20
  14. })

此时我们可以发现,ts会报错说,接口中不存在这样的一个参数:
在这里插入图片描述
但是!页面是肯定可以输出age的内容的。看一下js中的内容就明白了。

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

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

此时,我们可以发现,在传递参数时已经不会报错了,但是,如果我们想使用age,那肯定会报错了,因为在接口中不存在。
在这里插入图片描述
同样的,在页面中肯定能输出age的数据。

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

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

  1. interface FullName {
  2. firstName: string
  3. secondName: string
  4. age?:number
  5. }
  6. function printName(name:FullName):void{
  7. console.log(name.firstName + name.secondName);
  8. }
  9. function printAge(info:FullName):void{
  10. console.log(info.firstName + info.secondName + info.age);
  11. }
  12. printName({
  13. firstName: "李",
  14. secondName: "四"
  15. })
  16. // 传递参数没有顺序
  17. printAge({
  18. age: 20,
  19. secondName: "四",
  20. firstName: "李"
  21. })

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


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

  1. interface FullName {
  2. firstName: string
  3. secondName: string
  4. age?:number
  5. // 参数名是string类型,可以给它一个any类型数据
  6. [propname:string]:any
  7. }
  8. function printName(name:FullName):void{
  9. console.log(name.sex);
  10. }
  11. printName({
  12. firstName: "李",
  13. secondName: "四",
  14. sex: "男"
  15. })

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


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

实例:

  1. interface Config {
  2. type: string;
  3. url: string;
  4. data?:string;
  5. dataType: string;
  6. }
  7. // 原生js封装ajax,不支持ie6
  8. function ajax(config:Config){
  9. let xhr = new XMLHttpRequest();
  10. xhr.open(config.type,config.url, true);
  11. xhr.send(config.data);
  12. xhr.onreadystatechange = function(){
  13. if(xhr.readyState == 4 && xhr.status == 200){
  14. console.log('成功');
  15. if(config.dataType == 'json'){
  16. JSON.parse(xhr.responseText);
  17. }else{
  18. console.log(xhr.responseText)
  19. }
  20. }
  21. }
  22. }
  23. ajax({
  24. type: 'get',
  25. url: '', //填写相关api接口
  26. dataType: 'json',
  27. data: 'name=zhangsan'
  28. })

函数类型接口

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

  1. interface FullName {
  2. say():string
  3. }
  4. function printName(name:FullName):void{
  5. console.log(name.say())
  6. }
  7. printName({
  8. say(){
  9. return "我是李四"
  10. }
  11. })
  12. // 匿名函数类型接口
  13. interface encrypt {
  14. (key:string,value:string):string;
  15. }
  16. // 一定要遵守接口约束
  17. let md5:encrypt = function(key:string,value:string):string{
  18. // 模拟操作
  19. return key+value;
  20. }
  21. console.log(md5("name","李四"))

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

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

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


可索引接口

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

  1. // 可索引接口:对数组的约束
  2. interface UserArr{
  3. [index:number]:string;
  4. }
  5. let arr:UserArr = ['a','b','c'];
  6. console.log(arr[0]);
  7. // 可索引接口:对对象的约束
  8. interface UserObj{
  9. [index:string]:string;
  10. }
  11. let arr1:UserObj = { name: '李四'};
  12. console.log(arr1.name);

类类型接口

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

  1. interface Animal{
  2. name:string;
  3. eat(str:string):void;
  4. }
  5. // 实现接口用关键词implements
  6. class Dog implements Animal{
  7. name:string;
  8. constructor(name:string) {
  9. this.name = name;
  10. }
  11. // 必须实现方法,但是参数和数据类型不必相同
  12. eat():string{
  13. console.log(this.name + '吃肉')
  14. return this.name;
  15. }
  16. }
  17. let d = new Dog('旺财');
  18. console.log(d.eat());

接口扩展

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

  1. interface Animal{
  2. eat():void;
  3. }
  4. interface Person extends Animal{
  5. work():void;
  6. }
  7. // 实现接口用关键词implements
  8. class Web implements Person{
  9. name:string;
  10. constructor(name:string) {
  11. this.name = name;
  12. }
  13. // 不仅要实现相应接口中的属性和方法,
  14. // 还要实现接口继承的接口中的属性和方法,
  15. // 否则报错
  16. eat():void{
  17. console.log(this.name + '吃饭');
  18. }
  19. work():void{
  20. console.log(this.name + '工作');
  21. }
  22. }
  23. let my = new Web('李四');
  24. my.eat();
  25. my.work();

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

  1. interface Animal{
  2. eat():void;
  3. }
  4. interface Person extends Animal{
  5. work():void;
  6. }
  7. class Programmer{
  8. name:string;
  9. constructor(name:string) {
  10. this.name = name;
  11. }
  12. coding(code:string){
  13. console.log(this.name + code)
  14. }
  15. }
  16. class Web extends Programmer implements Person{
  17. constructor(name:string) {
  18. super(name)
  19. }
  20. eat():void{
  21. console.log(this.name + '吃饭');
  22. }
  23. work():void{
  24. console.log(this.name + '工作');
  25. }
  26. }
  27. let my = new Web('李四')
  28. my.coding('写ts代码')

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


最后还有一点需要说明:接口只是帮我们在TS中完成校验的功能,它并不会转化成JS代码。而反观抽象类,它会转化成相应的JS代码。感兴趣的话,快去TS中试试吧。

发表评论

表情:
评论列表 (有 0 条评论,602人围观)

还没有评论,来说两句吧...

相关阅读