【前端进阶】-TypeScript高级类型 | 交叉类型、索引签名类型、映射类型

news2025/10/31 2:48:04

前言

博主主页👉🏻蜡笔雏田学代码
专栏链接👉🏻【TypeScript专栏】
上篇文章讲解了TypeScript部分高级类型
详细内容请阅读如下:🔽
【前端进阶】-TypeScript高级类型 | 类的初始化、构造函数、继承、成员可见性
今天来学习TypeScript另外一些高级类型!
感兴趣的小伙伴一起来看看吧~🤞

在这里插入图片描述

文章目录

  • 类型兼容性
    • 对象之间的类型兼容性
    • 接口之间的类型兼容性
    • 函数之间的类型兼容性
  • 交叉类型
    • 交叉类型和接口继承的对比
  • 索引签名类型
  • 映射类型(in)
    • 索引查询类型(1 基本使用)
    • 索引查询类型(2 同时查询多个)

类型兼容性

两种类型系统:

  1. Structural Type System(结构化类型系统
  2. Nominal Type System(标明类型系统

TS采用的是结构化类型系统,也叫做ducktyping(鸭子类型),类型检查关注的是值所具有的形状

也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型

//两个类的兼容性演示
class Point {
    x: number
    y: number
}
class Point2D {
    x: number
    y: number
}
const p: Point = new Point2D()

解释:

  1. Point和Point2D是两个名称不同的类。
  2. 变量p的类型被显示标注为Point类型,但是,它的值却是Point2D的实例,并且没有类型错误。
  3. 因为TS是结构化类型系统,只检查Point和Point2D的结构是否相同(相同,都具有x和y两个属性,属性类型也相同)。
  4. 但是,如果在Nominal Type System中(比如,C#,Java等),它们是不同的类类型无法兼容

对象之间的类型兼容性

注意:在结构化类型系统中,如果两个对象具有相同的形状,则认为它们数属于同一类型,这种说法并不准确。

更准确的说法:对于对象类型来说,y的成员至少与x相同,则x兼容y(成员多的可以赋值给少的)。

//两个类的兼容性演示
class Point {
    x: number
    y: number
}
class Point2D {
    x: number
    y: number
}
const p: Point = new Point2D()

class Point3D {
    x: number
    y: number
    z: number
}
const p1: Point = new Point3D()

// 错误演示:
// const p2: Point3D = new Point()  类型 "Point" 中缺少属性 "z",但类型 "Point3D" 中需要该属性。

解释:

  1. Point3D的成员至少与Point相同,则Point兼容Point3D。
  2. 所以,成员多的Point3D可以赋值给成员少的Point

接口之间的类型兼容性

除了class之外,TS中的其他类型也存在相互兼容的情况,包括:1、接口兼容性 2、函数兼容性等。

  • 接口之间的兼容性,类似于class。并且,class和interface之间也可以兼容。(成员多的可以赋值给少的)
interface Point { x: number; y: number }
interface Point2D { x: number; y: number }
interface Point3D { x: number; y: number; z: number }

let p1: Point
let p2: Point2D
let p3: Point3D

p1 = p2
p1 = p3
p2 = p3
// 错误演示:
// p3 = p1    类型 "Point" 中缺少属性 "z",但类型 "Point3D" 中需要该属性。
// 类和接口之间也是兼容的
class Point4D { x: number; y: number; z: number }
p2 = new Point4D()

函数之间的类型兼容性

  • 函数之间兼容性比较复杂,需要考虑:1、参数个数 2、参数类型 3、返回值类型。

A. 参数个数,参数多的兼容参数少的(或者说,参数少的可以赋值给多的)。

// 1 参数个数:参数少的可以赋值给参数多的
type F1 = (a: number) => void
type F2 = (a: number, b: number) => void

let f1: F1
let f2: F2

f2 = f1
// 错误演示:
// f1 = f2
// 演示类型兼容性:
let arr = ['a','b','c']

arr.forEach(() => {})
arr.forEach(item => {})
arr.forEach((item,index) => {})
arr.forEach((item,index,array) => {})

解释:

  1. 参数少的可以赋值给参数多的,所以,f1可以赋值给f2。
  2. 数组forEach方法的第一个参数是回调函数,该实例中类型为:(value: string, index: number, array: string[])=>void
  3. 在JS中省略用不到的函数参数实际上是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性
  4. 并且因为回调函数是有类型的,所以,TS会自动推导出参数item,index,array的类型。

B. 参数类型,相同位置的参数类型要相同(原始类型)或兼容(对象类型)。

// 参数为原始类型
type F1 = (a: number) => void
type F2 = (a: number) => void
let f1: F1
let f2: F2
f1 = f2
f2 = f1

解释:函数类型F2兼容函数类型F1,因为F1和F2的第一个参数类型相同。

// 参数为对象类型
interface Point2D { x: number; y: number }
interface Point3D { x: number; y: number; z: number }

type F2 = (p: Point2D) => void //相当于有2个参数
type F3 = (p: Point3D) => void //相当于有3个参数

let f2: F2
let f3: F3

f3 = f2
// 错误示范:
// f2 = f3

解释:

  1. 注意,此处与前面讲到的接口兼容性冲突。
  2. 技巧:将对象拆开,把每个属性看做一个个参数,则,参数少的(f2)可以赋值给参数多的(f3)。

C. 返回值类型,只关注返回值类型本身即可:

// 3 返回值类型,只需要关注返回值类型本身即可
// 原始类型:
type F5 = () => string
type F6 = () => string

let f5: F5
let f6: F6

f6 = f5
f5 = f6

// 对象类型:
type F7 = () => { name: string }
type F8 = () => { name: string; age: number }

let f7: F7
let f8: F8

f7 = f8
// 错误演示:
// f8 = f7

解释:

  1. 如果返回值类型是原始类型,此时两个类型要相同,比如,左侧类型F5和F6。
  2. 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如,右侧类型F7和F8。

交叉类型

交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型常用于对象类型)。

比如,

interface Person {
    name: string
    say(): number
}
interface Contact {
    phone: string
}
type PersonDetail = Person & Contact 
let obj: PersonDetail = {
    name: 'jack',
    phone: 'aaa',
    say() {return 1}
}

解释:使用交叉类型后,新的类型PersonDetail就同时具备了Person和Contact的所有属性类型。

相当于,

type PersonDetail = { name: string; phone: string}

交叉类型和接口继承的对比

  • 相同点:都可以实现对象类型的组合。
  • 不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同
// 对比:
interface A {
  fn: ( value: number) => string
}
interface B extends A {
  fn: ( value: string) => string
}
//接口继承会报错:不能将类型“(value: string) => string”分配给类型“(value: number) => string”。

interface A {
        fn: ( value: number) => string
    }
interface B {
        fn: ( value: string) => string
    }   
type C = A & B

说明:以上代码,接口继承会报错(类型不兼容);交叉类型没有错误,可以简单地理解为:

let c: C = {
    fn(value: number | string) {
        return '1'
    }
}
//let c: C
//c.fn('a')
//c.fn(6)

索引签名类型

绝大多数情况下,我们都可以在使用对象前就确定对象的结构,并为对象添加准确的类型。

使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了

interface AnyObject {
  [key: string]: number
}
let obj: AnyObject = {
  a: 1,
  b: 2,
  cscs: 3
}

解释:

  1. 使用[key: string]来约束该接口中允许出现的属性名称。表示只要是string类型的属性名称,都可以出现在对象中。
  2. 这样,对象obj中就可以出现任意多个属性(比如,a,b等)。
  3. key只是一个占位符,可以换成任意合法的变量名称。
  4. 隐藏的前置知识:JS中对象({})的键是string类型的

在JS中数组是一类特殊的对象,特殊在数组的键(索引)是数值类型

并且,数组也可以出现任意多个元素。所以,在数组对应的泛型接口中,也用到了索引签名类型

interface MyArray<T> {
  [index: number]: T
}
let arr1: MyArray<number> = [1, 3, 5]
arr1[0]

解释:

  1. MyArray接口模拟原生的数组接口,并使用[n: number]来作为索引签名类型。
  2. 该索引签名类型表示:只要是number类型的键(索引)都可以出现在数组中,或者说数组中可以有任意多个元素。
  3. 同时也符合数组索引是number类型这一前提。

映射类型(in)

映射类型:基于旧类型创建新类型(对象类型),减少重复,提升开发效率。

比如,类型PropKeys有x/y/z,另一个类型Type1中也有x/y/z,并且Type1中x/y/z的类型相同:

type PropKeys = 'X' | 'y' | 'z'
type Type1 = { x:number; y: number; z: number }

这样书写没错,但x/y/z重复书写了两次。像这种情况,就可以使用映射类型来进行简化。

type PropKeys = 'x' | 'y' | 'z'
type Type2 = { [Key in PropKeys]: number }

解释:

  1. 映射类型是基于索引签名类型的,所以,该语法类似于索引签名类型,也使用了[]。
  2. Key in PropKeys表示Key可以是PropKeys联合类型中的任意一个,类似于forin(let k in obj)。
  3. 使用映射类型创建的新对象类型Type2和类型Type1结构完全相同。
  4. 注意:映射类型只能在类型别名中使用,不能在接口中使用。

映射类型除了根据联合类型创建新类型外,还可以根据对象类型来创建:(keyof)

type Props = { a: number; b: string; c: boolean }
type Type3 = { [key in keyof Props]: number }

解释:

  1. 首先,先执行keyof Props获取到对象类型Props中所有键的联合类型即,‘a’ | ‘b’ | ‘c’。
  2. 然后,Key in ...就表示Key可以是Props中所有的键名称中的任意一个。

在这里插入图片描述


分析泛型工具类型Partial的实现:

实际上,前面讲到的泛型工具类型(比如,Partial都是基于映射类型实现的)。

比如,Partial的实现:

type Partial<T> = {
  [P in keyof T]?: T[P]
}
type Props = { a: number; b: string; c: boolean }
type PartialProps = Partial<Props>

解释:

  1. keyof Tkeyof Props表示获取Props的所有键,也就是:‘a’ | ‘b’ | ‘c’。
  2. 在[]后面添加?(问号),表示将这些属性变为可选的,以此来实现Partial的功能。
  3. 冒号后面的T[P]表示获取T中每个键对应的类型。比如,如果是’a’则类型是number;如果是’b’则类型是string。
  4. 最终,新类型PartialProps和旧类型Props结构完全相同,只是让所有类型都变为可选了。

索引查询类型(1 基本使用)

刚刚用到的T[P]语法,在TS中叫做索引查询(访问)类型

作用:用来查询属性的类型。

type Props = { a: number; b: string; c: boolean }
type TypeA = Props['a']   //type TypeA = number

解释:Props[‘a’]表示查询类型Props中属性’a’对应的类型number。所以,TypeA的类型为number。

注意:[]中的属性必须存在于被查询类型中,否则就会报错。


索引查询类型(2 同时查询多个)

索引查询类型的其他使用方式:同时查询多个索引的类型。

type Props = { a: number; b: string; c: boolean }
type TypeA = Props['a' | 'b']

解释:使用字符串字面量的联合类型,获取属性a和b对应的类型,结果为:string|number。

type TypeA = Props[keyof Props]  //string | number | boolean

解释:使用keyof操作符获取Props中所有键对应的类型,结果为:string|number|boolean。

今天的分享就到这里啦✨ \textcolor{red}{今天的分享就到这里啦✨} 今天的分享就到这里啦

原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

🤞 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

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

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

相关文章

nvm安装node,配置npm 、cnpm

nvm 是什么&#xff1f; nodejs的版本管理工具&#xff0c;为了解决node.js各种版本存在不兼容现象可以通过它安装和切换不同版本的node.js 重要&#xff1a;完全卸载本地node&#xff0c; 下载链接 卸载完成之后&#xff0c;点击nvm-setup.exe安装版&#xff0c;直接运行n…

不是吧,阿sir,还有人不会制作影院订票系统前端页面吗?(拿来就用)

影院订票系统前端页面&#x1f389;案例分析&#x1f389;详细设计✨座位数据与样式定义✨座位的事件处理及相关的代码✨监听与数据格式化✨电影信息展示&#x1f389;动态操作演示图&#x1f389;源码&#xff08;附图片素材&#xff09;引言&#xff1a;大家好&#xff0c;欢…

npm和cnpm下载安装及VUE的创建

npm和cnpm下载安装及VUE的创建 1. node.js下载 node.js官网&#xff1a; http://nodejs.cn/download/ 下载安装后cmd输入以下命令查看版本 2. 配置npm 打开node.js的安装目录&#xff0c;我这里是D:\nodejs&#xff0c;在此目录下创建两个文件夹”node_global“和”node_ca…

【申请加入New Bing遇到的问题:当前无法使用此页面,cn.bing.com 重定向次数过多】

目录一.前言二.问题描述三.解决方案解决方案1:解决方案2:四.总结一.前言 前面的文章我们详细的讲解了如何加入New Bing&#xff0c;之前我们直接加入还可以直接访问&#xff0c;但是现在访问过多就会出现当前无法使用此页面&#xff0c;cn.bing.com 重定向次数过多的问题&…

eslint常见报错及解决

eslint常见报错问题1&#xff1a;Component name "index" should always be multi-word问题2&#xff1a;Newline required at end of file but not found问题3&#xff1a;Strings must use singlequote问题4&#xff1a;Expected indentation of 2 spaces but foun…

json-server|0编码实现REST API

欢迎来到我的博客 &#x1f4d4;博主是一名大学在读本科生&#xff0c;主要学习方向是前端。 &#x1f36d;目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏 &#x1f6e0;目前正在学习的是&#x1f525;React框架React框架React框架&#x1f525…

uniapp中的renderjs使用

需求是用openlayers在uniapp框架下做一个移动gis类的软件&#xff0c;选择用renderjs实现地图与dom的交互&#xff0c;一开始也是小白&#xff0c;通过百度资料以及一步步测试后掌握了renderjs的使用&#xff0c;以及地图功能的实现。 一、renderjs的作用是什么&#xff1f; r…

初始vue3

如今新vue项目首选用vue3 typescript vite pinia……模式。在使用Vue2时&#xff0c;使用的是选项式api进行vue项目的开发&#xff0c;vue3在这里做了重大的更新&#xff0c;vue3使用组合式api进行对项目实例化和构建。另外需要注意vue项目需要nodeJS环境的支持&#xff0c;…

LaTex(1):使用在线表格生成器工具生成LaTex表格

目录 0 在线工具 1 常用命令&#xff1a; 1.0 编辑表格 1.1 Table—set size可以改变表格大小&#xff1a; 1.2 合并表格和拆分表格&#xff1a; 1.3 生成latex代码与复制代码&#xff1a; 2 示例 0 在线工具 表格生成器网页&#xff1a;Create LaTeX tables online –…

BootStrap基本使用

目录 BootStrap框架 BootStrap特点 bootstrap的使用 布局容器 固定宽度 完整宽度 栅格网格系统 前言 列的形式 列组合和列偏移 列排序 列嵌套 排版 标题 段落 强调 对齐效果 列表 去点列表 内联列表 定义列表 代码 表格 表单标签 文本框和文本域 单…

【web服务】nginx为什么这么受企业欢迎?看完这边文章你就懂了

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是小鹏linux&#xff0c;运维领域创作者&#xff0c;阿里云ACE认证高级工程师&#x1f61c; &#x1f4dd; 个人主页&#xff1a;小鹏linux&#x1f525; &#x1f389; 支持我&#xff1a;点赞…

【ES6丨前端进阶基础 】ES6的关键字,新特性以及解构赋值

&#x1f482; 个人主页&#xff1a;Aic山鱼 个人社区&#xff1a;山鱼社区 &#x1f4ac; 如果文章对你有所帮助请点个&#x1f44d;吧!欢迎关注、点赞、收藏(一键三连)和订阅专目录 前言 什么是ecmascrpit 一&#xff0c;let关键字的特点 1.不能重复声明变量 2.块级作用…

Http协议之Content-Type理解

Content-Type&#xff0c;翻译过来就是”内容类型“&#xff0c;在互联网中就是”互联网媒体类型“。 在互联网中&#xff0c;两台计算机经常会传输数据&#xff0c;客户端会给服务器发数据&#xff0c;服务器也会给客户端发数据。数据的类型也是有很多种的&#xff0c;我们把所…

【微信小程序系列:四】前端利用wx.setStorageSync缓存设置有效时间

先言&#xff1a; 简单来说&#xff0c;就是利用缓存&#xff0c;进行有效期的保存&#xff0c;以此前端加以判断&#xff0c;在如登录状态过期&#xff0c;操作过期等场景使用&#xff0c;扩展性还蛮多的。 官方文档 实现&#xff1a; 原理&#xff1a;就是先设置一个缓存&…

项目实战 之 vue3 + vite + pinia

目录 一、创建项目 1. 安装vite 2. 创建项目 3. 安装过程 二、项目基本配置 1. 项目icon 2. 项目标题 3. 配置 jsconfig.json 4. 设置 .prettierrc 文件 5. 生成代码片段 01 - 网址 02 - 生成 03 - 配置 04 - 使用 三、项目目录 结构划分 1. assets 2. compo…

require.context()的用法详解

require.context&#xff08;&#xff09;的用法详解&#x1f334;require.context()的介绍&#x1f33a;用法一&#xff1a;在组件内引入多个组件&#x1f33c;用法二&#xff1a;在main.js中引入大量公共组件&#x1f342;用法三&#xff1a;使用插件注册全局组件&#x1f33…

axios+vue 请求时如何携带cookie

axiosvue 请求时如何携带cookie 1&#xff0c;当符合同源策略时&#xff0c;可以直接设置 document.cookie " 你要设置的内容 " mounted() {document.cookie "ioiopipoadiasdasdbasdbas"; // 非跨域传递cookie 直接设置cookie即可this.getData(); /…

Node.js安装,npm安装yarn步骤

第一步&#xff0c;首先安装npm npm是node.js下的包管理器&#xff0c;node.js的下载网址 Node.js 1.下载安装包后一路无脑点击next最后点击finish即可&#xff0c;安装完成之后打开文件夹就是以下目录。 2.在cmd窗口输入node -v、npm -v查看版本检查是否安装成功 一般完成以…

总结JS中常用的数组的方法大全

总结JS中常用的数组方法 JS中常用的数组方法总结 数组(Array)是一种复杂的数据类型&#xff0c;它属于Object(对象)类型&#xff0c;用来将一组数组合在一起&#xff0c;通过一个变量就可以访问一组数据。在使用数组时&#xff0c;经常会搭配循环语句使用&#xff0c;从而很方便…

Vue3+TS+Vite+Element Plus搭建后台管理系统

Vue3TSViteElement Plus搭建后台管理系统1、简介2、效果图3、技术栈4、项目目录5、setting.js(全局配置文件)6、路由router7、状态管理stores8、下载地址总结1、简介 该示例是vue3、typescript、vite、element plus搭建前端管理框架&#xff0c;主要模块分为&#xff1a;菜单、…