TypeScript进阶知识之接口(接口定义、接口属性、可索引类型、接口表示函数类型、额外的属性检查、接口继承、接口与类型别名的区别)

系列文章目录

引入一:Typescript基础引入(基础类型、元组、枚举)
引入二:Typescript面向对象引入(接口、类、多态、重写、抽象类、访问修饰符)
第一章:Typescript基础知识(Typescript介绍、搭建TypeScript环境、基本数据类型)
第二章:Typescript常用类型(任意值any、数组Array、函数Function、元组Tuple、类型推论、联合类型)
第三章:Typescript基础知识(类型断言、类型别名、字符串字面量类型、枚举、交叉类型)
第四章:Typescript基础知识(类型拓宽、类型缩小)
第五章:TypeScript进阶知识之类(类的定义、类的基本使用、类的构造函数、类的属性和方法、访问修饰符、类的继承、抽象类)
第六章:TypeScript进阶知识之接口(接口定义、接口属性、可索引类型、接口表示函数类型、额外的属性检查、接口继承、接口与类型别名的区别)


文章目录

  • 系列文章目录
  • 一、接口定义
  • 二、接口属性
    • 2.1 可选属性
    • 2.2 只读属性
    • 2.3 任意属性
  • 三、可索引类型
  • 四、接口表示函数类型
  • 五、额外的属性检查
    • 5.1 什么是额外的属性检查
    • 5.2 绕开额外的属性检查
  • 六、接口相关的继承
    • 6.1 类实现接口
    • 6.2 接口继承接口
    • 6.3 接口继承类
  • 七、接口与类型别名的区别

一、接口定义

TypeScript 中,接口(Interface)是一种用来定义对象的结构和行为的类型。通过接口,我们可以定义对象应该有哪些属性、属性的类型以及方法。

  • 使用关键字 interface 来定义接口。
  • 声明接口后,直接使用接口名称作为变量的类型。
  • 方法的定义和函数的定义类似,包括参数和返回值类型。

二、接口属性

2.1 可选属性

  • 带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ? 符号。

    interface Person {name: string;age?: number; // 可选属性
    }const p1: Person = { name: "Alice" };
    const p2: Person = { name: "Bob", age: 25 };
    
  • 可选属性的好处有2个:

    • 可以对可能存在的属性进行预定义
    • 可以捕获引用了不存在的属性时的错误

2.2 只读属性

  • 有时候我们希望某些属性在对象创建后不能被修改,可以将这些属性声明为只读属性。

  • 通过在属性名称前面加上 readonly 关键字,就可以将属性设置为只读。

    interface Point{readonly x:number,readonly y:number,
    }let point:Point={x:10,y:20,
    }point.x=100;// Error:Cannot assign to 'x' because it is a read-only property.
    

    上述例子中,声明了一个Point的接口,接口中的属性 x 和 y 都是只读的,然后创建了一个 point 对象,类型为 Point,此时,我们不能再给对象中的 x 和 y 重新赋值,会报错,因为它们都是只读属性。

  • TypeScript 还提供了 ReadonlyArray 类型,它与 Array
    相似,只是把所有可变方法去掉了,确保数组创建后再也不能被修改

    let a:number[]=[1,2,3];
    let a2:ReadonlyArray<number>=a;a2[0]=100;//Error:Index signature in type 'readonly number[]' only permits reading.
    

2.3 任意属性

  • 一个接口中除了包含必选和可选属性之外,还允许有其他的任意的属性,这时我们可以使用 索引签名 的形式来满足上述要求。

    interface Person{name:string,age?:number,//Error:Property 'age' of type 'number | undefined' is not assignable to 'string' index type 'string'.[propName:string]:string,
    }let p:Person={name:'Tom',age:25,gender:'male',location:'Shanghai'
    }
    

    上述例子中,任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了。

  • 注意1:一旦定义了任意属性,那么必选属性和可选属性的类型都必须是它的类型的子集

  • 注意2:一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型。

    interface Person {name: string;age?: number; // 这里age真实的类型应该为:number | undefined[propName: string]: string | number | undefined;
    }let person: Person = {name: 'Echo',age: 25,gender: 'male'
    }
    

三、可索引类型

  • 接口可以描述具有索引签名的对象,这样我们就可以通过索引来访问对象的属性。

    interface StringArray {[index: number]: string;
    }let myArray: StringArray = ["Bob", "Fred"];let myStr: string = myArray[0];//Bob
    

    TypeScript 支持两种索引签名:字符串和数字可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。 也就是说用 100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致。

四、接口表示函数类型

  • 使用接口表示函数类型,我们需要给接口定义一个调用签名。 它 就像是一个只有参数列表和返回值类型的函数 定义,参数列表里的每个参数都需要名字和类型。

    interface Fun{(name:string,age:number):string
    }
    

    在上述例子中,Fun是一个接口,它表示一个接收两个参数 numberage,参数类型都为 string,并且返回值为 string 类型的函数。这样定义后,我们可以像使用这个接口定义函数类型的变量了。

    下面的例子创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。

    interface Fun{(name:string,age:number):string
    }const fun:Fun=(name:string,age:number)=>{return name + ' is ' + age + ' years old';
    }
    
  • 注意:对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。

    interface Fun{(name:string,age:number):string
    }const fun: Fun = (n: string, a: number) => {return n + ' is ' + a + ' years old';
    };
    

    函数的参数会逐个进行检查,要求对应位置上的参数类型是兼容的。

  • 如果不想指定类型,TypeScript 的类型系统会推断出参数类型,因为函数直接赋值给了 Fun 类型变量。函数的返回值类型是通过其返回值推断出来的

    interface Fun{(name:string,age:number):string
    }const fun: Fun = (n, a) => {return n + ' is ' + a + ' years old';
    };
    
  • 对象里面通常有函数方法,同样也可以在接口里面定义,在接口里定义的函数参数、返回值,在使用的时候必须按照约束传入。

    interface Person {name: string;age: number;say: (message: string) => void;
    }let p: Person = {name: 'Tom',age: 25,say: (message: string) => {console.log(message);},
    };p.say('hello');//hello
    

五、额外的属性检查

5.1 什么是额外的属性检查

  • 当我们使用对象字面量赋值给接口类型时,TypeScript 会自动进行额外的属性检查。这意味着赋值的对象不能包含接口中未定义的额外属性,否则会导致编译错误。

    interface Person {name: string;
    }let p:Person  = { name: 'Tome', age: 25 };//Error
    

5.2 绕开额外的属性检查

  • 鸭式辨型法
    就是通过制定规则来判定对象是否实现这个接口。

    interface Person {name: string;
    }// let p:Person  = { name: 'Tome', age: 25 };function printName(person: Person) {console.log(person.name);
    }let p = { name: 'Tome', age: 25 };printName(p);printName({ name: 'Tome', age: 25 });//Error
    

    上面代码,在参数里写对象就相当于是直接给person赋值,这个对象有严格的类型定义,所以不能多参或少参。而当你在外面将该对象用另一个变量p接收,p不会经过额外属性检查,但会根据类型推论为let p: { name: string; age: number} = { name: 'Tome', age: 25 };,然后将这个p再赋值给person,此时根据类型的兼容性,两种类型对象,参照鸭式辨型法,因为都具有name属性,所以被认定为两个相同,故而可以用此法来绕开多余的类型检查。

  • 类型断言
    类型断言的意义就等同于你在告诉程序,你很清楚自己在做什么,此时程序自然就不会再进行额外的属性检查了。

    interface Person{name:string,age:number,money?:number
    }let p1:Person={name:'Tom',age:25,money:100,sex:0,
    } as Person
    
  • 索引标签

    interface Person{name:string,age:number,money?:number, [key:string]:any,
    }let p2:Person={name:'Tom',age:25,money:100,sex:0,
    } 
    

六、接口相关的继承

6.1 类实现接口

实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候 不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。

举例:门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它。

interface Alarm {alert(): void;
}class Door {
}class SecurityDoor extends Door implements Alarm {alert() {console.log('SecurityDoor alert');}
}class Car implements Alarm {alert() {console.log('Car alert');}
}

一个类可以实现多个接口。就好比一个车不只有报警功能,还有开关车灯的功能:

interface Alarm {alert(): void;
}interface Light {lightOn(): void;lightOff(): void;
}class Car implements Alarm, Light {alert() {console.log('Car alert');}lightOn() {console.log('Car light on');}lightOff() {console.log('Car light off');}
}

6.2 接口继承接口

接口与接口之间可以是继承关系,A接口要继承B接口的属性,可以通过extends关键字继承。

通过继承,子接口可以获得父接口中定义的属性和方法,并可以在自身接口中添加新的属性和方法。

interface Shape {color: string;
}interface Circle extends Shape {radius: number;getArea(): number;
}const circle: Circle = {color: "red",radius: 5,getArea() {return Math.PI * this.radius * this.radius;}
}

在上面的例子中,使用 extends 关键字实现了接口 Circle 继承 Shape。继承后,Circle 就有了 Shape 中的 color 属性,以及自身的 radius 属性以及 getArea() 方法。

6.3 接口继承类

常见的面向对象语言中,接口是不能继承类的,但是在 TypeScript 中却是可以的:

class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}
}interface Point3d extends Point {z: number;
}let point3d: Point3d = {x: 1, y: 2, z: 3};

实际上,当我们在声明 class Point 时,除了会创建一个名为 Point 的类之外,同时也创建了一个名为 Point 的类型(实例的类型)

  • 所以我们既可以将 Point 当做一个类来用(使用 new Point 创建它的实例)

    class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}
    }const p = new Point(1, 2);
    
  • 也可以将 Point 当做一个类型来用(使用 : Point 表示参数的类型)

    class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}
    }function printPoint(p: Point) {console.log(p.x, p.y);
    }printPoint(new Point(1, 2));
    

    这个例子实际上可以等价于:

    class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}
    }interface PointInstanceType {x: number;y: number;
    }function printPoint(p: PointInstanceType) {console.log(p.x, p.y);
    }printPoint(new Point(1, 2));
    

    上例中我们新声明的 PointInstanceType 类型,与声明 class Point 时创建的 Point 类型是等价的。

  • 所以回到 Point3d 的例子中,我们就能很容易的理解为什么 TypeScript 会支持接口继承类了:

    class Point {x: number;y: number;constructor(x: number, y: number) {this.x = x;this.y = y;}
    }interface PointInstanceType {x: number;y: number;
    }// 等价于 interface Point3d extends PointInstanceType
    interface Point3d extends Point {z: number;
    }let point3d: Point3d = {x: 1, y: 2, z: 3};
    

    上述案例中,当我们声明 interface Point3d extends Point 时,Point3d 继承的实际上是类 Point 的实例的类型。

所以「接口继承类」和「接口继承接口」没有什么本质的区别

值得注意的是,PointInstanceType 相比于 Point,缺少了 constructor 方法,这是因为 声明 Point 类时创建的 Point 类型是不包含构造函数的。另外,除了构造函数是不包含的,静态属性或静态方法也是不包含的(实例的类型当然不应该包括构造函数、静态属性或静态方法)

换句话说,声明 Point 类时创建的 Point 类型只包含其中的实例属性和实例方法:

class Point {/** 静态属性,坐标系原点 */static origin = new Point(0, 0);/** 静态方法,计算与原点距离 */static distanceToOrigin(p: Point) {return Math.sqrt(p.x * p.x + p.y * p.y);}/** 实例属性,x 轴的值 */x: number;/** 实例属性,y 轴的值 */y: number;/** 构造函数 */constructor(x: number, y: number) {this.x = x;this.y = y;}/** 实例方法,打印此点 */printPoint() {console.log(this.x, this.y);}
}interface PointInstanceType {x: number;y: number;printPoint(): void;
}let p1: Point;
let p2: PointInstanceType;

上例中最后的类型 Point 和类型 PointInstanceType 是等价的。同样的,在接口继承类的时候,也只会继承它的实例属性和实例方法。

七、接口与类型别名的区别

  • 相同点: 都可以用于定义对象的结构和类型。

  • 不同点:

    • 接口更适合用于描述真实存在的对象,而类型别名更适合用于定义复杂的类型。

    • 接口可以被其他对象实现,而类型别名只是给类型起了一个别名。

    • 两者都可以用来描述对象或函数的类型,但是语法不同。

    interface Point {x: number;y: number;
    }interface SetPoint {(x: number, y: number): void;
    }type Point = {x: number;y: number;
    };type SetPoint = (x: number, y: number) => void;
    
    • 与接口不同,类型别名还可以用于其他类型,如基本类型(原始值)、联合类型、元组。
    // 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;
    
    • 与类型别名不同,接口可以定义多次,会被自动合并为单个接口。
    interface Point { x: number; }
    interface Point { y: number; }
    const point: Point = { x: 1, y: 2 };
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/106737.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅谈安科瑞无线测温设备在俄罗斯某项目的应用

摘要&#xff1a;安科瑞ATE系列和ARTM-Pn无线测温设备适用于高低压柜的梅花触头&#xff0c;线缆&#xff0c;母排等位置对温度的实时监测。 Abstract: ATE series and ARTM-Pn are suitable for monitoring the real-time temperature of circuit breaker contact,cable,busb…

跨境电商:为民营经济注入新活力

中国的民营经济一直以来都是国家经济发展的中流砥柱&#xff0c;而近年来&#xff0c;跨境电商产业崭露头角&#xff0c;为民营经济注入了新的活力和机遇。本文将探讨跨境电商如何成为中国民营企业的助推引擎&#xff0c;以及其对民营经济的积极影响。 民营经济的支柱地位 中国…

ChatGPT AIGC 完成Excel跨多表查找操作vlookup+indirect

VLOOKUP和INDIRECT的组合在Excel中用于跨表查询,其中VLOOKUP函数用于在另一张表中查找数据,INDIRECT函数则用于根据文本字符串引用不同的工作表。具体操作如下: 1.假设在工作表1中,A列有你要查找的值,B列是你希望查询的工作表名称。 2.在工作表1的C列输入以下公式:=VLO…

iMeta框架使用方法

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是「奇点」&#xff0c;江湖人称 singularity。刚工作几年&#xff0c;想和大家一同进步&#x1f91d;&#x1f91d; 一位上进心十足的【Java ToB端大厂…

判断非线性负载是否合格的方法可以从以下几个方面进行考虑:

额定功率容量&#xff1a;需要了解负载设备的额定功率容量&#xff0c;根据负载设备的规格和说明书&#xff0c;确定其额定功率容量是否能够满足实际需求&#xff0c;如果超过了负载设备的额定功率容量&#xff0c;可能会导致设备过载&#xff0c;从而影响其正常运行。 电压波形…

JVM 垃圾回收机制(可达性分析、引用计数)

目录 1 什么是垃圾2 为什么需要回收3 哪些对象被判定为垃圾呢3.1 引用计数法3.2 可达性分析算法&#xff1a;GC Roots根 1 什么是垃圾 垃圾是指在运行程序中没有任何指针指向的对象&#xff0c;就是需要被回收的。 2 为什么需要回收 执行程序会不断地分配内存空间&#xff0c…

分布式事务协调中间件---seata快速入门

分布式事务 Seata&#xff0c;之前叫做Fescar&#xff0c;是一个开源的分布式事务解决方案&#xff0c;它主要致力于提供高效和简单的分布式事务服务。Seata主要用于解决微服务架构下的数据一致性问题。 Seata 的基本原理是基于两阶段提交 (2PC) 以及三阶段提交 (3PC)&#xff…

宏定义实现二进制数的奇偶位交换

思路分析 通过宏定义来实现二进制数的奇偶位交换&#xff0c;如果一个个遍历交换的话&#xff0c;那得算到猴年马月&#xff0c;这是我在网上看到的一个思路&#xff1a; 我们将每一位&#xff08;整数在计算机里存储是4字节&#xff0c;32位&#xff09;二进制数的奇数位保留…

nodejs+vue水浒鉴赏平台系统

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

EtherCat

百度安全验证 EtherCAT&#xff08;Ethernet for Control Automation Technology&#xff09;是一种高性能、实时性强的工业以太网通信协议&#xff0c;它可以实现多个从站设备通过一个主站设备进行控制和通信。 主站&#xff1a; EtherCAT主站是指控制整个EtherCAT网络的设备…

C++ 反向迭代器

反向迭代器的即正向迭代器的--&#xff0c;反向迭代器的--即正向迭代器的&#xff0c;反向迭代器和正向迭代器的很多功能都是相似的&#xff0c;因此我们可以复用正向迭代器作为反向迭代器的底层容器来封装&#xff0c;从而实现出反向迭代器&#xff0c;即&#xff1a;反向迭代…

vue3后台管理框架之Mock开发

前言 在前后端对接中,有时后端的接口数据没有 那么快能给出,因此我们可以通过mock模拟自己的请求数据,在后端接口没有给出的同时,先使用mock请求的数据完成前端相关的逻辑 官方文档:vite-plugin-mock vite 的数据模拟插件,是基于 vite.js 开发的。 并同时支持本地环境…

并查集-合并集合

文章目录 QuestionIdeasCode Question 一共有 n 个数&#xff0c;编号是 1∼n &#xff0c;最开始每个数各自在一个集合中。 现在要进行 m 个操作&#xff0c;操作共有两种&#xff1a; M a b&#xff0c;将编号为 a 和 b 的两个数所在的集合合并&#xff0c;如果两个数已经…

【LeetCode 算法专题突破】双指针(⭐)

文章目录 前言1. 移动零题目描述代码 2. 复写零题目描述代码 3. 快乐数题目描述代码 4. 盛最多水的容器题目描述代码 5. 有效三角形的个数题目描述代码 6. 三数之和题目描述代码 7. 四数之和题目描述代码 总结 前言 学算法入门必学的一个章节&#xff0c;双指针算法&#xff0…

计算机网络-计算机网络体系结构-数据链路层

目录 *一、组帧 1.1字符计数法 1.2字符填充法 1.3零比特填充法 1.4违规编码 *二、差错控制 2.1检错编码 2.2.1奇偶校验码 2.2.2 CRC循环冗余码 2.2纠错编码-海明码 *三、流量控制和可靠传输机制 流量控制 停止-等待协议 ​编辑 后退n帧协议的滑动窗口(GBN) 选择…

ChatGPT AIGC 制作大屏可视化分析案例

第一部分提示词prompt: 商品 价格 p1 13 p2 41 p3 42 p4 53 p5 19 p6 28 p7 92 p8 62 城市 销量 北京 69 上海 13 南京 18 武汉 66 成都 70 你现在是一名非常专业的数据分析师,请结合上述数据完成下列几件事情 1:第一部分数…

基于 Triple 实现 Web 移动端后端全面打通

*作者&#xff1a;陈有为&#xff0c;陌陌研发工程师、Apache Dubbo PMC RPC 协议开发微服务 在我们正常开发微服务的时候&#xff0c;传统 RPC 服务可能在最底层。上层可能是浏览器、移动端、外界的服务器、自己的测试、curl 等等。我们可能会通过 Tomcat 这种外部服务器去组…

机器视觉知识讲的深不如讲的透

我深思这个话题&#xff0c;大家来培训&#xff0c;其实培训机构也很痛苦&#xff0c;每个热掌握的参差不齐&#xff0c;你说他不会吧&#xff0c;会一点电气&#xff0c;你说他会吧&#xff0c;会一点Opencv&#xff0c;会一点visionpro,会一点Visionmaster,会一点Halcon。好像…

【Retinex theory】【图像增强】-笔记

1 前言 retinex 是常见的图像增强的方法&#xff0c;retinex 是由两个单词合成的&#xff1a;retina conrtex &#xff0c;即视网膜皮层。 2 建立的基础 Land 的 retinex theory 建立在三个假设之下&#xff1a; 真实世界是无色的&#xff0c;我们所谓的颜色是光和物质相互…

Flink之源算子Data Source

源算子Data Source 概述内置Data Source基于集合构建基于文件构建基于Socket构建 自定义Data SourceSourceFunctionRichSourceFunction 常见连接器第三方系统连接器File Source连接器DataGen Source连接器Kafka Source连接器RabbitMQ Source连接器MongoDB Source连接器 概述 Fl…