前端 TS 语法 接口(2)

介绍

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

只读属性     readonly

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

interface Point {readonly x: number;readonly y: number;
}

你可以通过赋值一个对象字面量来构造一个Point。 赋值后, xy再也不能被改变了。

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

TypeScript具有ReadonlyArray<T>类型,它与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改:

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

上面代码的最后一行,可以看到就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:

a = ro as number[];

readonly vs const

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

 

接口  可选属性    例如 color?: string; 

问号(?)用于属性定义

可选属性的含义:使用这个属性时,要么这个属性名不存在,要么必须符合属性的类型定义 

接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。 可选属性在应用“option bags”模式时很常用,即给函数传入的参数对象中只有部分属性赋值了。

下面是应用了“option bags”的例子:

interface SquareConfig {color?: string;width?: number;
}function createSquare(config: SquareConfig): {color: string; area: number} {let newSquare = {color: "white", area: 100};if (config.color) {newSquare.color = config.color;}if (config.width) {newSquare.area = config.width * config.width;}return newSquare;
}let mySquare = createSquare({color: "black"});

 

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

可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。 比如,我们故意将 createSquare里的color属性名拼错,就会得到一个错误提示:

interface SquareConfig {color?: string;width?: number;
}function createSquare(config: SquareConfig): { color: string; area: number } {let newSquare = {color: "white", area: 100};if (config.color) {// Error: Property 'collor' does not exist on type 'SquareConfig'newSquare.color = config.collor;  // Type-checker can catch the mistyped name here}if (config.width) {newSquare.area = config.width * config.width;}return newSquare;
}let mySquare = createSquare({color: "black"});

函数类型

接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。

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

interface SearchFunc {(source: string, subString: string): boolean;
}

这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {let result = source.search(subString);if (result == -1) {return false;}else {return true;}
}

对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。 比如,我们使用下面的代码重写上面的例子:

let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {let result = src.search(sub);if (result == -1) {return false;}else {return true;}
}

函数的参数会逐个进行检查,要求对应位置上的参数类型是兼容的。 如果你不想指定类型,Typescript的类型系统会推断出参数类型,因为函数直接赋值给了 SearchFunc类型变量。 函数的返回值类型是通过其返回值推断出来的(此例是 falsetrue)。 如果让这个函数返回数字或字符串,类型检查器会警告我们函数的返回值类型与SearchFunc接口中的定义不匹配。

let mySearch: SearchFunc;
mySearch = function(src, sub) {let result = src.search(sub);if (result == -1) {return false;}else {return true;}
}

可索引的类型

与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型,比如a[10]ageMap["daniel"]。 可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。 让我们看一个例子:

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

上面例子里,我们定义了StringArray接口,它具有索引签名。 这个索引签名表示了当用 number去索引StringArray时会得到string类型的返回值。

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

class Animal {name: string;
}
class Dog extends Animal {breed: string;
}// Error: indexing with a 'string' will sometimes get you a Dog!
interface NotOkay {[x: number]: Animal;[x: string]: Dog;
}

字符串索引签名能够很好的描述dictionary模式,并且它们也会确保所有属性与其返回值类型相匹配。 因为字符串索引声明了 obj.propertyobj["property"]两种形式都可以。 下面的例子里, name的类型与字符串索引类型不匹配,所以类型检查器给出一个错误提示:

interface NumberDictionary {[index: string]: number;length: number;    // 可以,length是number类型name: string       // 错误,`name`的类型不是索引类型的子类型
}

最后,你可以将索引签名设置为只读,这样就防止了给索引赋值:

interface ReadonlyStringArray {readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!

你不能设置myArray[2],因为索引签名是只读的。

类类型

实现接口

与C#或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约。

interface ClockInterface {currentTime: Date;
}class Clock implements ClockInterface {currentTime: Date;constructor(h: number, m: number) { }
}

你也可以在接口中描述一个方法,在类里实现它,如同下面的setTime方法一样:

interface ClockInterface {currentTime: Date;setTime(d: Date);
}class Clock implements ClockInterface {currentTime: Date;setTime(d: Date) {this.currentTime = d;}constructor(h: number, m: number) { }
}

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

类静态部分与实例部分的区别

当你操作类和接口的时候,你要知道类是具有两个类型的:静态部分的类型和实例的类型。 你会注意到,当你用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误:

interface ClockConstructor {new (hour: number, minute: number);
}class Clock implements ClockConstructor {currentTime: Date;constructor(h: number, m: number) { }
}

这里因为当一个类实现了一个接口时,只对其实例部分进行类型检查。 constructor存在于类的静态部分,所以不在检查的范围内。

因此,我们应该直接操作类的静态部分。 看下面的例子,我们定义了两个接口, ClockConstructor为构造函数所用和ClockInterface为实例方法所用。 为了方便我们定义一个构造函数 createClock,它用传入的类型创建实例。

interface ClockConstructor {new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {tick();
}function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {return new ctor(hour, minute);
}class DigitalClock implements ClockInterface {constructor(h: number, m: number) { }tick() {console.log("beep beep");}
}
class AnalogClock implements ClockInterface {constructor(h: number, m: number) { }tick() {console.log("tick tock");}
}let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

因为createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 7, 32)里,会检查AnalogClock是否符合构造函数签名。

扩展接口

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

interface Shape {color: string;
}interface Square extends Shape {sideLength: number;
}let square = <Square>{};
square.color = "blue";
square.sideLength = 10;

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

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

混合类型

先前我们提过,接口能够描述JavaScript里丰富的类型。 因为JavaScript其动态灵活的特点,有时你会希望一个对象可以同时具有上面提到的多种类型。

一个例子就是,一个接口可以同时做为函数和对象使用,并带有额外的属性。

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

在使用JavaScript第三方库的时候,你可能需要像上面那样去完整地定义类型。

接口继承类

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。

这是很有用的,当你有一个很深层次的继承,但是只想你的代码只是针对拥有特定属性的子类起作用的时候。子类除了继承自基类外与基类没有任何联系。 例:

class Control {private state: any;
}interface SelectableControl extends Control {select(): void;
}class Button extends Control implements SelectableControl {select() { }}class TextBox extends Control {select() { }}// 错误:“Image”类型缺少“state”属性。class Image implements SelectableControl {select() { }}class Location {}

在上面的例子里,SelectableControl包含了Control的所有成员,包括私有成员state。 因为 state是私有成员,所以只能够是Control的子类们才能实现SelectableControl接口。 因为只有 Control的子类才能够拥有一个声明于Control的私有成员state,这对私有成员的兼容性是必需的。

Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。 实际上,SelectableControl就像Control一样,并拥有一个select方法。 ButtonTextBox类是SelectableControl的子类(因为它们都继承自Control并有select方法),但ImageLocation类并不是这样的。

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

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

相关文章

Java反转单链表

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ //核心思想&#xff0c;利用cur和Curnex…

基础篇_数据持久化(实战-我的B站,MySQL数据库)

文章目录 一. 实战-我的B站1. 功能演示2. 设计数据类数据展示路径参数 3. 设计 Service 类静态资源映射读取文件的时机Stream API 改进 二. MySQL 数据库1. 数据库必要性2. MySQL 安装下载压缩包初始化数据库运行服务器运行客户端 3. 初步使用4. datagrip添加数据源导入数据用 …

Python 爬虫的基本原理(一)

饼干 与 会话&#xff08;狗头&#xff09; Python 爬虫的基本原理涉及以下几个关键步骤&#xff1a; HTTP 请求&#xff1a;爬虫首先发送一个 HTTP 请求到目标网站。这可以通过 Python 的库如 requests 实现。请求可以是 GET 或 POST 类型&#xff0c;用于获取网页内容或提交…

路由前置守卫router.beforeEach相关用法

router.beforeEach 是 Vue Router 提供的全局前置守卫&#xff0c;用于在路由切换之前执行一些逻辑。该守卫接收三个参数&#xff1a; to: 即将要进入的目标路由对象。from: 当前导航正要离开的路由。next: 一个函数&#xff0c;用于 resolve 钩子。调用 next 表示路由可以继续…

HBase 基础

HBase 基础 HBase1. HBase简介1.1 HBase定义1.2 HBase数据模型1.2.1 HBase逻辑结构1.2.2 HBase物理存储结构1.2.3 数据模型 1.3 HBase基本架构 2. HBase环境安装2.1 HBase 安装部署2.1.1 HBase 本地按照2.1.2 HBase 伪分布模式安装2.1.3 HBase 集群安装 2.2 HBase Shell操作2.2…

jar包部署到linux虚拟机的docker中之后连不上mysql

前言&#xff1a; 跟着黑马学习docker的时候&#xff0c;将java项目部署到了docker中&#xff0c;运行访问报错&#xff0c;反馈连不上mysql。 错误描述&#xff1a; 方法解决&#xff1a; 概述&#xff1a;在虚拟中中&#xff0c;我进入项目容器的内部&#xff0c;尝试ping…

分布式搜索引擎--认识

elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 。 elasticsearch结合kibana、Logstash、Beats&#xff0c;也就是elastic stack&#xff08;ELK&#xff09;。被广泛…

【嵌入式移植】3、编译U-Boot

编译U-Boot 0 U-Boot及本文所选硬件1 获取U-Boot源码2 获取工具链3 BL314 编译4.1 yylloc4.2 u_boot_dtsi 5 烧写6 上电验证 0 U-Boot及本文所选硬件 Das U-Boot&#xff0c;全称 Universal Boot Loader&#xff0c;是遵循GPL条款的开放源码项目。U-Boot的作用是系统引导。U-B…

达梦数据实时同步工具DMHS常见故障处理

1.启动失败 DMHS在同步故障或是重启时&#xff0c;如果启动失败&#xff0c;则有可能是下面几个原因&#xff1a; 1)EXEC或CPT模块加载失败EXEC和CPT模块由于需要和数据库进行交互&#xff0c;那么它在启动时需要依赖数据库的ODBC和OCI驱动&#xff0c;如果它加载失败则说明它…

seata分布式事务(与dubbo集成)

1.seata是什么? Seata 是一款开源的分布式事务解决方案&#xff0c;致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 2.seata的注解 GlobalTransactional&#xff1a;全局事务注解&#xff0c;添加了以后可实现分布式事务的回滚和提交&#xff0c;用法与spring…

【.NET Core】Lazy<T> 实现延迟加载详解

【.NET Core】Lazy 实现延迟加载详解 文章目录 【.NET Core】Lazy<T> 实现延迟加载详解一、概述二、Lazy<T>是什么三、Lazy基本用法3.1 构造时使用默认的初始化方式3.2 构造时使用指定的委托初始化 四、Lazy.Value使用五、Lazy扩展用法5.1 实现延迟属性5.2 Lazy实现…

翱翔与深耕:鸟型与青蛙型数学家在学科发展中的互补共生与卓越贡献

在弗里曼•戴森的知名演讲《鸟和青蛙》中&#xff0c;他以富有诗意的比喻揭示了数学家们的两种迥异特质与角色定位。戴森将那些具有前瞻视野、能够跨越学科界限&#xff0c;力图构建统一理论框架的数学家形象地比作“鸟”&#xff0c;而将专注于解决具体问题&#xff0c;深入挖…

MySQL 日志之二进制日志-binlog

1、简介 MySQL 的二进制日志记录了对 MySQL 所有的更改操作&#xff0c;不包括 select 和 show 等操作。二进制日志文件主要有&#xff1a;数据恢复、主从复制、审计&#xff08;判断是否有注入攻击&#xff09;等作用。 2、二进制日志参数配置 2.1、文件参数配置 linux 中 My…

安装nvidia driver出现 the cc vision check falied

这里提示说的需要gcc12,但是我只有gcc11,所以就报错了&#xff0c;说一说我自己的解决方法&#xff1a; 安装gcc12和g12,再切换版本为gcc12 安装gcc12: sudo apt install gcc-12安装g12: sudo apt -y install g-12切换版本&#xff1a;参考博客

惯性导航---里程计非完整性约束

惯性导航—里程计非完整性约束 1 非完整性约束原理 在进行管道中心线定位时&#xff0c;惯性导航系统在初始化后通过不断地力学编排更新载体的姿态、速度和位置信息&#xff0c;但是由于传感器是惯性器件&#xff0c;其导航误差会不断累积&#xff0c;这便需要借助外界观测量辅…

Python 分支结构之if语句

在之前编写的Python中我们都是一条一条的编写的语句&#xff0c;没有任何条件或判断&#xff0c;程序会按照代码编写顺序依次执行&#xff0c;这种编写结构叫顺序结构&#xff0c;是编程中最基本的控制结构。除了顺序机构&#xff0c;还有一个比较基本的控制结构&#xff0c;那…

Linux的SSH服务

一.SSH服务简介 1.什么是SSH SSH&#xff08;Secure Shell&#xff09;是一种安全通道协议&#xff0c;主要用来实现字符界面的远程登录、远程复制等功能。SSH 协议对通信双方的数据传输进行了加密处理&#xff0c;其中包括用户登录时输入的用户口令&#xff0c;SSH 为建立在应…

【Linux技术专题】「夯实基本功系列」带你一同学习和实践操作Linux服务器必学的Shell指令(文件处理指令-上)

文件处理指令-上 背景前言专栏介绍面向对象重点内容文件处理命令file格式[options] 主要参数简单说明使用案例 mkdir格式[options] 主要参数应用实例 grep格式主要参数[optionsl 主要参数 应用实例pattern正则表达式主要参数 应用实例fgrep和egrep dd格式[options]主要参数 应用…

linux 网络设置

查看linux基础的网络配置 命令 网关route -nip 地址ifconfig / ip aDNS 服务器cat /etc/resolv.conf主机名hostname路由route -n网络连接状态ss / netstat 一&#xff0c;ifconfig 查看网络接口信息 &#xff08;一&#xff09;ifconfig …

设计模式—— 单例设计模式

单例设计模式 什么是单例模式 单例模式是一种对象创建型模式&#xff0c;使用单例模式&#xff0c;可以保证为一个类只生成唯一的实例对象。也就是说&#xff0c;在整个程序空间中&#xff0c;该类只存在一个实例对象。 为什么使用单例模式 在应用系统开发中&#xff0c;我…