9.【TypeScript 教程】接口(Interface)

TypeScript 接口(Interface)

本节介绍 TypeScript 各种类型接口的声明及其使用方法,接口在 TypeScript 中是极其重要的,我们使用接口来定义契约,如类型命名、属性检查、函数类型定义等。

在下一节学习完类之后,你会知道类也可以作为接口来使用。接口的种类繁多,在学习过程中一定要亲手编写,以达到灵活使用。

1. 解释

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。——官方定义

接口是对 JavaScript 本身的随意性进行约束,通过定义一个接口,约定了变量、类、函数等应该按照什么样的格式进行声明,实现多人合作的一致性。TypeScript 编译器依赖接口用于类型检查,最终编译为 JavaScript 后,接口将会被移除。

// 语法格式
interface DemoInterface {}

2. 应用场景

在声明一个对象函数或者时,先定义接口,确保其数据结构的一致性。

在多人协作时,定义接口尤为重要。

3. 接口的好处

过去我们写 JavaScript 定义一个函数:

function getClothesInfo(clothes) {console.log(clothes.price)
}let myClothes = {color: 'black', size: 'XL', price: 98 
}
getClothesInfo(myClothes)

之前我们写 JavaScript 这样是很正常的,但同时你可能会遇到下面这些问题:

getClothesInfo() // Uncaught TypeError: Cannot read property 'price' of undefined
getClothesInfo({ color: 'black' }) // undefined

相信原因你也知道,JavaScript 是 弱类型 语言,并不会对传入的参数进行任何检测,错误在运行时才被发现。那么通过定义 接口,在编译阶段甚至开发阶段就避免掉这类错误,接口将检查类型是否和某种结构做匹配。

3.1 举例说明

下面通过接口的方式重写之前的例子:

实例演示

interface Clothes {color: string;size: string;price: number;
}function getClothesInfo(clothes: Clothes) {console.log(clothes.price)
}let myClothes: Clothes = { color: 'black', size: 'XL', price: 98 
}getClothesInfo(myClothes)

代码解释: 代码中,定义了一个接口 Clothes,在传入的变量 clothes 中,它的类型为 Clothes。这样,就约束了这个传入对象的 外形 与接口定义一致。只要传入的对象满足上面的类型约束,那么它就是被允许的。

Tips:

  1. 定义接口要 首字母大写

  2. 只需要关注值的 外形,并不像其他语言一样,定义接口是为了实现。

  3. 如果没有特殊声明,定义的变量比接口少了一些属性是不允许的,多一些属性也是不允许的,赋值的时候,变量的形状必须和接口的形状保持一致。

4. 接口的属性

4.1 可选属性

接口中的属性不全是必需的。可选属性的含义是该属性在被变量定义时可以不存在。

// 语法
interface Clothes {color?: string;size: string;price: number;
}// 这里可以不定义属性 color
let myClothes: Clothes = { size: 'XL', price: 98 
}

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

这时,仍不允许添加未定义的属性,如果引用了不存在的属性时 TS 将直接捕获错误。

4.2 只读属性

一些对象属性只能在对象刚刚创建的时候修改其值。你可以在属性名前用 readonly 来指定只读属性,比如价格是不能被修改的:

// 语法
interface Clothes {color?: string;size: string;readonly price: number;
}// 创建的时候给 price 赋值
let myClothes: Clothes = { size: 'XL', price: 98 
}// 不可修改
myClothes.price = 100
// error TS2540: Cannot assign to 'price' because it is a constant or a read-only property

TypeScript 可以通过 ReadonlyArray<T> 设置数组为只读,那么它的所有写方法都会失效。

let arr: ReadonlyArray<number> = [1,2,3,4,5];
arr[0] = 6; // Index signature in type 'readonly number[]' only permits reading

代码解释: 代码中的泛型语法在之后会有专门的小节介绍。

4.2.1 readonly vs const

最简单判断该用 readonly 还是 const 的方法是看要把它做为变量使用还是做为一个属性。做为 变量 使用的话用 const,若做为 属性 则使用 readonly。

4.3 任意属性

有时候我们希望接口允许有任意的属性,语法是用 [] 将属性包裹起来:

// 语法
interface Clothes {color?: string;size: string;readonly price: number;[propName: string]: any;
}// 任意属性 activity
let myClothes: Clothes = { size: 'XL', price: 98,activity: 'coupon'
}

代码解释: 这里的接口 Clothes 可以有任意数量的属性,并且只要它们不是 color size 和 price,那么就无所谓它们的类型是什么。

  • 项目案例:使用 axios 库发起 HTTP 传输的时候,可以写入一个自定义的属性,就是因为源码中定义了一个任意属性:
this.$axios({method: 'put',url: '/cms/user',data: {nickname: this.nickname,},showBackend: true,
})

5. 函数类型

除了描述带有属性的普通对象外,接口也可以描述函数类型。

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

interface SearchFunc {(source: string, subString: string): boolean;
}let mySearch: SearchFunc;
mySearch = function(source: string, subString: string): boolean {return source.search(subString) > -1;
}

对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。你可以改变函数的参数名,只要保证函数参数的位置不变。函数的参数会被逐个进行检查:

interface SearchFunc {(source: string, subString: string): boolean;
}let mySearch: SearchFunc;
// source => src, subString => sub
mySearch = function(src: string, sub: string): boolean {return src.search(sub) > -1;
}

如果你不想指定类型,TypeScript 的类型系统会推断出参数类型,因为函数直接赋值给了 SearchFunc 类型变量。

interface SearchFunc {(source: string, subString: string): boolean;
}let mySearch: SearchFunc;
mySearch = function(src, sub) {let result = src.search(sub);return result > -1;
}

如果接口中的函数类型带有函数名,下面两种书写方式是等价的:

interface Calculate {add(x: number, y: number): numbermultiply: (x: number, y: number) => number
}

6. 可索引类型

可索引类型接口读起来有些拗口,直接看例子:

// 正常的js代码
let arr = [1, 2, 3, 4, 5]
let obj = {brand: 'imooc',type: 'education'
}arr[0]
obj['brand']

再来看定义可索引类型接口:

interface ScenicInterface {[index: number]: string
}let arr: ScenicInterface = ['西湖', '华山', '故宫']
let favorite: string = arr[0]

示例中索引签名是 number类型,返回值是字符串类型。

另外还有一种索引签名是 字符串类型。我们可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。通过下面的例子理解这句话:

// 正确
interface Foo {[index: string]: number;x: number;y: number;
}// 错误
interface Bar {[index: string]: number;x: number;y: string; // Error: y 属性必须为 number 类型
}

代码解释:

第 12 行,语法错误是因为当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。也就是说用 100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致。

7. 类类型

我们希望类的实现必须遵循接口定义,那么可以使用 implements 关键字来确保兼容性。

这种类型的接口在传统面向对象语言中最为常见,比如 java 中接口就是这种类类型的接口。这种接口与抽象类比较相似,但是接口只能含有抽象方法和成员属性,实现类中必须实现接口中所有的抽象方法和成员属性。

interface AnimalInterface {name: string;
}class Dog implements AnimalInterface {name: string;constructor(name: string){this.name = name}
}

你也可以在接口中描述一个方法,在类里实现它:

interface AnimalInterface {name: stringeat(m: number): string
}class Dog implements AnimalInterface {name: string;constructor(name: string){this.name = name}eat(m: number) {return `${this.name}吃肉${m}分钟`}
}

接口描述了类的公共部分,而不是公共和私有两部分。 它不会帮你检查类是否具有某些私有成员。

8. 继承接口

和类一样,接口也可以通过关键字 extents 相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。

一个接口可以继承多个接口,创建出多个接口的合成接口。

interface Shape {color: string;
}interface PenStroke {penWidth: number;
}interface Square extends Shape, PenStroke {sideLength: number;
}let square = {} as Square;
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

9. 混合类型

在前面已经介绍,接口可以描述函数、对象的方法或者对象的属性。

有时希望一个对象同时具有上面提到多种类型,比如一个对象可以当做函数使用,同时又具有属性和方法。

interface Counter {(start: number): string;interval: number;reset(): void;
}function getCounter(): Counter {let counter = function (start: number) { } as Counter;counter.interval = 123;counter.reset = function () { };return counter;
}let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

代码解释:

第 1 行,声明一个接口,如果只有 (start: number): string 一个成员,那么这个接口就是函数接口,同时还具有其他两个成员,可以用来描述对象的属性和方法,这样就构成了一个混合接口。

第 7 行,创建一个 getCounter() 函数,它的返回值是 Counter 类型的。

let counter = function (start: number) { } as Counter;

第 8 行,通过类型断言,将函数对象转换为 Counter 类型,转换后的对象不但实现了函数接口的描述,使之成为一个函数,还具有 interval 属性和 reset() 方法。断言成功的条件是,两个数据类型只要有一方可以赋值给另一方,这里函数类型数据不能赋值给接口类型的变量,因为它不具有 interval 属性和 reset() 方法。

类型断言在之后的小节也会单节介绍。

10. 小结

本节介绍了接口的基本用法及其使用场景,接口在 TypeScript 中至关重要,TypeScript 编译器依赖接口用于类型检查。

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

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

相关文章

机器学习~从入门到精通(三)梯度下降法

一、梯度下降法 # 梯度下降不是一种算法&#xff0c;是一种最优化方法 # 上节课讲解的梯度下降的案例 是一个简单的一元二次方程 # 最简单的线性回归&#xff1a;只有一个特征的线性回归&#xff0c;有两个theta # 二、在多元线性回归中使用梯度下降求解 三、### R…

系统学习Python——警告信息的控制模块warnings:为新版本的依赖关系更新代码

分类目录&#xff1a;《系统学习Python》总目录 在默认情况下&#xff0c;主要针对Python开发者&#xff08;而不是Python应用程序的最终用户&#xff09;的警告类别&#xff0c;会被忽略。 值得注意的是&#xff0c;这个“默认忽略”的列表包含DeprecationWarning&#xff08…

vuex是什么?怎么使用?哪种功能场景使用它?

Vuex是Vue.js官方推荐的状态管理库&#xff0c;用于在Vue应用程序中管理和共享状态。它基于Flux架构和单向数据流的概念&#xff0c;将应用程序的状态集中管理&#xff0c;使得状态的变化更可追踪、更易于管理。Vuex提供了一个全局的状态树&#xff0c;以及一些用于修改状态的方…

求斐波那契数列矩阵乘法的方法

斐波那契数列 先来简单介绍一下斐波那契数列&#xff1a; 斐波那契数列是指这样一个数列&#xff1a;1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13&#xff0c;21&#xff0c;34&#xff0c;55&#xff0c;89……这个数列从第3项开始 &…

[NAND Flash 6.3] NAND FLASH基本编程(写)操作及原理_NAND FLASH Program Operation 源码实现

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< 全文 3244 字 ​ 前言 使用的NAND FLASH的硬件原理图,面对这些引脚,很难明白他们是什么含义,下面先来个热身: 问1. 原理图上NAND FLASH只有数据线,怎…

行业内参~移动广告行业大盘趋势-2023年12月

前言 2024年&#xff0c;移动广告的钱越来越难赚了。市场竞争激烈到前所未有的程度&#xff0c;小型企业和独立开发者在巨头的阴影下苦苦挣扎。随着广告成本的上升和点击率的下降&#xff0c;许多原本依赖广告收入的创业者和自由职业者开始感受到前所未有的压力。 &#x1f3…

使用pygame实现简单的烟花效果

import pygame import sys import random import math# 初始化 Pygame pygame.init()# 设置窗口大小 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption("Fireworks Explosion")# 定义颜色 black (0, 0, 0) wh…

基于Java SSM框架实现在线作业管理系统项目【项目源码】计算机毕业设计

基于java的SSM框架实现在线作业管理系统演示 JSP技术 JSP技术本身是一种脚本语言&#xff0c;但它的功能是十分强大的&#xff0c;因为它可以使用所有的JAVA类。当它与JavaBeans 类进行结合时&#xff0c;它可以使显示逻辑和内容分开&#xff0c;这就极大的方便了运动员的需求…

【DIY summaries on Linux】

DIY Linux summaries 1) difference between ways of creation file and edit files1.1) echoecho talk to yourself touch 1) difference between ways of creation file and edit files 1.1) echo ###) > echo talk to yourself touch

数据治理工程师CDGA数据架构

1. 企业架构不包括哪项&#xff1f;&#xff08;C &#xff09; A 业务架构 B 数据架构 C 系统架构 D 技术架构 2. 关于架构设计生命周期描述错误的是&#xff1f;( D ) A 可以是针对当前的 B 可以是面向未来的 C 可以是已实施完成的 D 可以是已经退役 10 年的 3. 关于企业数据…

IPv6组播--SSM Mapping

概念 SSM(Source-Specific Multicast)称为指定源组播,要求路由器能了解成员主机加入组播组时所指定的组播源。 如果成员主机上运行MLDv2,可以在MLDv2报告报文中直接指定组播源地址。但是某些情况下,成员主机只能运行MLDv1,为了使其也能够使用SSM服务,组播路由器上需要提…

k8s-数据卷

存储卷----数据卷 容器内的目录和宿主机的目录进行挂载 容器在系统上的生命周期是短暂的&#xff0c;delete&#xff0c;k8s用控制创建的pod&#xff0c;delete相当于重启&#xff0c;容器的状态也会恢复到初识状态 一旦容器回到初始状态&#xff0c;所有得分后天编辑的文件…

粒子群算法优化RBF神经网络回归分析

目录 完整代码和数据下载链接:粒子群算法优化RBF神经网络回归分析(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88738570 RBF的详细原理 RBF的定义 RBF理论 易错及常见问题 RBF应用实例,基于rbf的空调功率预测 代码 结果分析 展望…

通过Wireshark抓包分析谈谈DNS域名解析的那些事儿

原创/朱季谦 本文主要想通过动手实际分析一下是如何通过DNS服务器来解析域名获取对应IP地址的&#xff0c;毕竟&#xff0c;纸上得来终觉浅&#xff0c;绝知此事要躬行。 一、域名与IP地址 当在浏览器上敲下“www.baidu.com”时&#xff0c;一键回车&#xff0c;很快&#x…

Linux远程登陆协议ssh

目录 一、SSH服务 1. ssh基础 2. 原理 3. 服务端配置 3.1 常用配置项 3.2 具体操作 3.2.1 修改默认端口号 3.2.2 禁止root用户登录 3.2.3 白名单列表 3.2.4 黑名单列表 3.2.5 使用秘钥对及免交互验证登录 3.2.6 免交互式登录 一、SSH服务 1. ssh基础 SSH&…

VQE音频处理流程

VQE 上行VQE&#xff0c;主要针对MIC采集部分的音频增强 下行VQE&#xff0c;主要针对SPK播放部分的音频增强 附关键词解释 RES RES 模块为重采样&#xff08;Resampler&#xff09;模块。当AI上行或AO下行通路中开启VQE 各功能 模块时&#xff0c;在处理前后各存在一次重采样…

c语言实现b树

概述&#xff1a;B 树&#xff08;B-tree&#xff09;是一种自平衡的搜索树数据结构&#xff0c;广泛应用于数据库和文件系统等领域。它的设计旨在提供一种高效的插入、删除和查找操作&#xff0c;同时保持树的平衡&#xff0c;确保各个节点的深度相差不大。 B 树的特点包括&a…

怎么使用好爬虫IP代理?爬虫代理IP有哪些使用技巧?

在互联网时代&#xff0c;爬虫技术被广泛应用于数据采集和处理。然而&#xff0c;在使用爬虫技术的过程中&#xff0c;经常会遇到IP被封禁的问题&#xff0c;这给数据采集工作带来了很大的困扰。因此&#xff0c;使用爬虫IP代理成为了解决这个问题的有效方法。本文将介绍如何使…

Java中泛型和Object类型 初级进阶教程(一)

在学习的过程中&#xff0c;常常看到某个类或者接口等中使用 List<T>, Test<T>&#xff0c;其中<T>的作用是什么呢&#xff1f; 1 在类中使用泛型 public class Box<T> {private T content;public void setContent(T content) {this.content conten…

深度学习与药物发现在健康衰老中的应用

深度学习是一种模拟人脑神经网络处理信息的方法&#xff0c;其已在语音、图像、自然语言等处理中发挥了巨大作用。但在健康衰老这一领域的应用中&#xff0c;它面临着很多挑战。 首先&#xff0c;健康衰老是一个复杂的生物学过程&#xff0c;涉及众多因素&#xff0c;如基因、…