TypeScript 学习笔记(二):接口与类型别名、字面量类型

news2024/10/7 18:24:57

一、接口的定义

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

二、接口的用途

接口的用途就是对行为和动作进行规范和约束,跟抽象类有点像,但是,接口中不能有方法体,只允许有方法定义。

三、接口用法:

1. 使用interface来定义接口:

  interface Info {
    firstName: string;
    lastName: string;
  }
  const getFullName = ({ firstName, lastName }: Info) => {
    return `${firstName} ${lastName}`;
  };
  console.log(getFullName({ firstName: '123', lastName: '1231' }));

注意在定义接口的时候,你不要把它理解为是在定义一个对象,而要理解为{}括号包裹的是一个代码块,里面是一条条声明语句,只不过声明的不是变量的值而是类型。声明也不用等号赋值,而是冒号指定类型。每条声明之前用换行分隔即可,或者也可以使用分号或者逗号,都是可以的。

2. 可选属性

接口设置可选属性,在属性名后面加个?即可:

interface Vegetables {
  color?: string;
  type: string;
}
 

3. 多余属性检查

getVegetables({
  type: "tomato",
  size: "big" // 'size'不在类型'Vegetables'中
});
 

我们看到,传入的参数没有 color 属性,但也没有错误,因为它是可选属性。但是我们多传入了一个 size 属性,这同样会报错,TypeScript 会告诉你,接口上不存在你多余的这个属性。只要接口中没有定义这个属性,就会报错,但如果你定义了可选属性 size,那么上面的例子就不会报错。

4. 绕开多余属性检查

  • 什么是接口的多余参数检查
interface Baseinfo {
    name:string,
    sex?:string
}
// 人 
function printPesonInfo(parmasinfo: Baseinfo) {
    console.log(`姓名:${parmasinfo.name }`)
}

// 如果直接传递参数,且传递的参数key未在接口中定义会提示错误
printPesonInfo( {name:'wang',age:13} ) // 报错的

在这里插入图片描述

  • 使用类型断言

类型断言就是用来明确告诉 TypeScript,我们已经自行进行了检查,确保这个类型没有问题,希望 TypeScript 对此不进行检查,所以最简单的方式就是使用类型断言:

interface Baseinfo {
    name:string,
    sex?:string
}
// 人 
function printPesonInfo(parmasinfo: Baseinfo) {
    console.log(`姓名:${parmasinfo.name }`)
}

// 利用类型断言,告诉编译器我们传递的参数 就是Baseinfo 接口的东西
printPesonInfo( {name:'wang',age:13} as Baseinfo ) // wang
  • 索引签名
interface Baseinfo {
    name:string,
    sex?:string,
    [other:string]:any
}
// 人 
function printPesonInfo(parmasinfo: Baseinfo) {
    console.log(`姓名:${parmasinfo.name }`)
}

// 接口中的索引签名other 就会收到age
printPesonInfo( {name:'wang',age:13}) // wang
  • 利用类型兼容性
interface Baseinfo {
    name:string,
    sex?:string,
}
// 人 
function printPesonInfo(parmasinfo: Baseinfo) {
    console.log(`姓名:${parmasinfo.name }`)
}

let paramsinfo = {name:'wang',age:13} 
// 类型兼容性就是我们定义的paramsinfo 不管有都少东西,只要包含接口中定义的即可
printPesonInfo(paramsinfo) // 姓名:wang

5. 只读属性

关键字:readonly

const NAME: string = "Lison";
NAME = "Haha"; // Uncaught TypeError: Assignment to constant variable
 
const obj = {
  name: "lison"
};
obj.name = "Haha";
 
interface Info {
  readonly name: string;
}
const info: Info = {
  name: "Lison"
};
info["name"] = "Haha"; // Cannot assign to 'name' because it is a read-only property
 

6. 函数类型

接口可以描述普通对象,还可以描述函数类型,我们先看写法:

interface AddFunc {
  (num1: number, num2: number): number;
}

这里我们定义了一个AddFunc结构,这个结构要求实现这个结构的值,必须包含一个和结构里定义的函数一样参数、一样返回值的方法,或者这个值就是符合这个函数要求的函数。我们管花括号里包着的内容为调用签名,它由带有参数类型的参数列表和返回值类型组成。后面学到类型别名一节时我们还会学习其他写法。来看下如何使用:

const add: AddFunc = (n1, n2) => n1 + n2;
const join: AddFunc = (n1, n2) => ${n1} ${n2}; // 不能将类型'string'分配给类型'number'
add("a", 2); // 类型'string'的参数不能赋给类型'number'的参数
 

上面我们定义的add函数接收两个数值类型的参数,返回的结果也是数值类型,所以没有问题。而join函数参数类型没错,但是返回的是字符串,所以会报错。而当我们调用add函数时,传入的参数如果和接口定义的类型不一致,也会报错。

你应该注意到了,实际定义函数的时候,名字是无需和接口中参数名相同的,只需要位置对应即可。

四、类型别名

类型别名就是给一种类型起个别的名字,之后只要使用这个类型的地方,都可以用这个名字作为类型代替,但是它只是起了一个名字,并不是创建了一个新类型。这种感觉就像 JS 中对象的赋值,你可以把一个对象赋给一个变量,使用这个对象的地方都可以用这个变量代替,但你并不是创建了一个新对象,而是通过引用来使用这个对象。

使用 type 关键字,定义类型别名:

type TypeString = string;
let str: TypeString;
str = 123; // error Type '123' is not assignable to type 'string'
 
  • 类型别名也可以使用泛型:
type PositionType<T> = { x: T; y: T };
const position1: PositionType<number> = {
  x: 1,
  y: -1
};
const position2: PositionType<string> = {
  x: "right",
  y: "top"
};
 
  • 使用类型别名时也可以在属性中引用自己:
type Child<T> = {
  current: T;
  child?: Child<T>;
};
let ccc: Child<string> = {
  current: "first",
  child: {
    // error
    current: "second",
    child: {
      current: "third",
      child: "test" // 这个地方不符合type,造成最外层child处报错
    }
  }
};
 

在这里插入图片描述

  • 但是要注意,只可以在对象属性中引用类型别名自己,不能直接使用,比如下面这样是不对的:
type Child = Child[]; // error 类型别名“Child”循环引用自身

在这里插入图片描述
因为类型别名只是为其它类型起了个新名字来引用这个类型,所以当它为接口起别名时,不能使用 extendsimplements

  • 接口和类型别名有时可以起到同样作用,比如下面这个例子:
type Alias = {
  num: number;
};
interface Interface {
  num: number;
}
let alias: Alias = {
  num: 123
};
let interface: Interface = {
  num: 321
};
alias = interface;
 

可以看到用类型别名和接口都可以定义一个只包含 num 属性的对象类型,而且类型是兼容的。那么什么时候用类型别名,什么时候用接口呢?可以通过两点来选择:

  1. 当你定义的类型要用于拓展,即使用 implements 等修饰符时,用接口。
  2. 当无法通过接口,并且需要使用联合类型或元组类型,用类型别名。

五、字面量类型

1. 字符串字面量类型

字符串字面量类型其实就是字符串常量,与字符串类型不同的是它是具体的值。

type Name = "Lison";
const name1: Name = "test"; // error 不能将类型“"test"”分配给类型“"Lison"”
console.log(name1);
const name2: Name = "Lison";
console.log(name2); 

在这里插入图片描述
使用联合类型来使用多个字符串:

type Direction = "north" | "east" | "south" | "west";
function getDirectionFirstLetter(direction: Direction) {
  return direction.substr(0, 1);
}
getDirectionFirstLetter("test"); // error 类型“"test"”的参数不能赋给类型“Direction”的参数
getDirectionFirstLetter("east");
 

2. 数字字面量类型

另一个字面量类型就是数字字面量类型,它和字符串字面量类型差不多,都是指定类型为具体的值。

type Age = 18;
interface Info {
  name: string;
  age: Age;
}
const info: Info = {
  name: "Lison",
  age: 28 // error 不能将类型“28”分配给类型“18”
};
 

这里补充一个比较经典的逻辑错误,来看例子:

function getValue(index: number) {
  if (index !== 0 || index !== 1) {
    // error This condition will always return 'true' since the types '0' and '1' have no overlap
    // ...
  }
}
 

在这里插入图片描述

这个例子中,在判断逻辑处使用了 || 符,当 index !== 0 不成立时,说明 index 就是 0,则不应该再判断 index 是否不等于 1;而如果 index !== 0 成立,那后面的判断也不会再执行;所以这个地方会报错。

六、Interface 与 Type 的区别

1. 区别

  1. 接口可以重复定义的接口类型,它的属性会叠加,类型别名不行
interface Language {
  id: number
}

interface Language {
  name: string
}

let lang: Language = {
  id: 1, // ok

  name: 'name', // ok
}

// 如果使用类型别名
/** ts(2300) 重复的标志 */
type Language = {
  id: number
}

/** ts(2300) 重复的标志 */
type Language = {
  name: string
}
let lang: Language = {
  id: 1,
  name: 'name',
}
  1. type 可以使用联合类型和交集,interface 不能使用联合类型和交集组合
type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
  1. type 支持类型映射,interface不支持
type Keys = "firstname" | "surname"

type DudeType = {
  [key in Keys]: string
}

const test: DudeType = {
  firstname: "Pawel",
  surname: "Grzybek"
}

// 报错
//interface DudeType {
//  [key in keys]: string
//}

2. 相同点

都允许拓展(extends)

interface 和 type 都可以拓展,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 extends interface , 虽然效果差不多,但是两者语法不同。

  • interface extends interface
interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
  • type extends type
type Name = { 
  name: string; 
}
type User = Name & { age: number  };
  • interface extends type
type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
  • type extends interface
interface Name { 
  name: string; 
}
type User = Name & { 
  age: number; 
}

总结:
interface 只能用于定义对象类型和方法,而 type 的声明方式除了对象之外还可以定义交叉、联合、原始类型等,类型声明的方式适用范围显然更加广泛。

但是interface也有其特定的用处:
interface 方式可以实现接口的 extends 和 implements
interface 可以实现接口合并声明

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

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

相关文章

qt 聊天室

服务器端 widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);//实例化一个服务器对象server new QTcpServer(this);}Widget::~Widget() {delete ui; }…

ELK插件介绍

ELK插件介绍 一、Grok 正则捕获插件1、概述2、内置正则表达式调用3、自定义表达式调用 二、multiline 多行合并插件1、概念2、安装3、使用 multiline 插件 三、date 时间处理插件1、概念2、操作3、时间戳详解 四、mutate 数据修改插件1、概念2、案例 一、Grok 正则捕获插件 1、…

上海亚商投顾:沪指震荡反弹 新能源车产业链再度爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 沪指今日震荡反弹&#xff0c;创业板指午后涨近1%。新能源车产业链再度爆发&#xff0c;整车、零部件、智能驾驶等…

python接口自动化(二十九)--html测试报告通过邮件发出去——上(详解)

简介 前边几篇&#xff0c;已经教小伙伴们掌握了如何生成HTML的测试报告&#xff0c;那么生成测试报告&#xff0c;我们也不能放在那里不管了&#xff0c;这样即使你报告在漂亮&#xff0c;领导也看不到。因此如果想向领导汇报工作&#xff0c;不仅需要提供更直观的测试报告。而…

大学生活动社交小程序开发笔记(1)

可研分析 大学生活动社交小程序是一种基于移动互联网的社交平台&#xff0c;旨在为大学生提供一个方便、快捷、安全的社交和活动交流平台 功能规划 活动发布&#xff1a;平台可以发布将要举行的活动&#xff0c;包括时间、地点、费用等信息&#xff0c;并邀请其他用户参加。…

《UNUX环境高级编程》(8)进程控制

1、引言 2、进程标识 每个进程都用一个唯一的非负整数标识&#xff0c;即为进程id&#xff1a;pid。进程ID是可以复用的&#xff0c;当一个进程终止时&#xff0c;其进程ID就可以用来标识其他进程。系统中有一些专用进程&#xff1a; 进程ID为0的是调度进程&#xff0c;也称交…

【软件测试】Git 远程仓库的使用(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 查看远程仓库 想…

Docker笔记 容器的数据卷

1. 数据卷概念 思考&#xff1a; Docker容器删除后&#xff0c;在容器中产生的数据还在吗&#xff1f; 答案是不在了&#xff0c;数据存放在容器中&#xff0c;如果将容器删除&#xff0c;数据也会被一并删除 Docker容器和外部机器可以直接交换文件吗&#xff1f; 答案是不…

为什么需要Promises ?

同步"异步操作", 避免Block多层嵌套造成的"回调地狱" The problem with async code (without Promises) Typically, async operations take a completion handler in a form of a block, which is called to provide either a result or an error. To per…

银河麒麟服务器v10 sp1 部署 redis 及redis gui 客户端工具

上一篇&#xff1a;银河麒麟服务器v10 sp1 redis开机自动启动_csdn_aspnet的博客-CSDN博客 本文介绍另一种redis安装方式及客户端工具安装。 Redis 是一种内存数据模型存储&#xff0c;可用作数据库、缓冲区和消息传递中继。它是开源的&#xff08;BSD 许可&#xff09;。字符…

热爱python的第一天:初识python,搭建python环境,运行第一个python小程序

目录 1 初始python python 概述 python的应用领域 应用python的公司 2 搭建python 开发环境 2.1 安装python&#xff08;以windows 系统为例&#xff09;&#xff08;1&#xff09;下载安装包 &#xff08;2&#xff09; 下载保存后打开文件夹点击以管理员身份运行 &…

IntegrityError: FOREIGN KEY constraint failed解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

nginx漏洞修复之检测到目标URL存在http host头攻击漏洞

漏洞说明 为了方便的获得网站域名&#xff0c;开发人员一般依赖于HTTP Host header。例如&#xff0c;在php里用_SERVER[“HTTP_HOST”]。但是这个header是不可信赖的&#xff0c;如果应用程序没有对host header值进行处理&#xff0c;就有可能造成恶意代码的传入。 解决方法…

安全狗亮相2023第二届上海网络安全博览会

7月5日至7日&#xff0c;“新耀东方-2023第二届上海网络安全博览会暨高峰论坛”在上海顺利举办。此次大会由上海市信息网络安全管理协会、国家计算机网络应急技术处理协调中心上海分中心、(ISC)2上海分会、上海市普陀区科学技术委员会、上海市网络安全产业示范园共同主办。 作…

Keil5开发STM32F4

Gitee keil工程 软件下载和安装&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1PWDAU0EhVZ6J8h6xH_uw5g 提取码&#xff1a;b343 MDK526.exe Keil.STM32F4xx_DFP.2.17.1.pack keygen_2032.exe JLink_Windows_V640.exe en.stsw-stm32065_v1-7-1_STM32F4 &…

【数据仓库】Windows源码安装DataEase,DataEase二次开发

上文记录了DataEase入门使用指南&#xff0c;本文主要记录Windows下源码安装及二次开发步骤【数据仓库】BI看板DataEase入坑指南_wenchun001的博客-CSDN博客 改动文件 源码 GitHub release 链接: Releases dataease/dataease GitHub SDK 软件环境 后端&#xff1a; JDK …

MongoDB快速入门

虽说现在关系型数据库还是主流&#xff0c;但是面对某些需求的时候&#xff0c;需要非关系型数据库来补充它&#xff0c;学习一个主流的NoSQL数据库还是很有必要的。MongoDB是一个功能丰富的NoSQL数据库&#xff0c;本文整理了它最常用的部分形成了这篇入门教程&#xff0c;希望…

设计模式——命令模式

命令模式 定义 将一个请求封装成一个对象&#xff0c;从而让你使用不同的请求吧客户端参数化&#xff0c;对请求排队或者记录请求日志&#xff0c;可以提供命令的撤销和恢复功能。 命令模式是一个高内聚的模式。 优缺点、应用场景 优点 类间解耦。调用者与接收者之间没有任…

探索AI领域,AI图像安全技术助力行业健康发展

目录 一、AI时代降临二、AIOCR与传统OCR技术三、通过人工智能模型生成AI图片技术探索四、提前布局&#xff0c;合合信息AI图像安全技术助力行业健康发展1、识别医疗门诊发票和报告2、图像篡改检测升级&#xff0c;截图篡改检测3、AIGC判别&#xff0c;人脸伪造检测4、OCR对抗攻…

WebSocket使用记录

使用视频地址 1、添加前端使用文件 2、后端配置 2.1添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>2.2添加websocket配置类 import org.spri…