首页IT科技交叉索引怎么用(【前端进阶】-TypeScript高级类型 | 交叉类型、索引签名类型、映射类型)

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

时间2025-06-16 12:00:03分类IT科技浏览4144
导读:前言...

前言

博主主页👉🏻蜡笔雏田学代码

专栏链接👉🏻【TypeScript专栏】

上篇文章讲解了TypeScript部分高级类型

详细内容请阅读如下:🔽

【前端进阶】-TypeScript高级类型 | 类的初始化              、构造函数                   、继承      、成员可见性

今天来学习TypeScript另外一些高级类型!

感兴趣的小伙伴一起来看看吧~🤞

类型兼容性

两种类型系统:

Structural Type System(结构化类型系统) Nominal Type System(标明类型系统)

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

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

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

解释:

Point和Point2D是两个名称不同的类       。 变量p的类型被显示标注为Point类型         ,但是      ,它的值却是Point2D的实例                    ,并且没有类型错误         。 因为TS是结构化类型系统            ,只检查Point和Point2D的结构是否相同(相同   ,都具有x和y两个属性                    ,属性类型也相同)                   。 但是               ,如果在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" 中需要该属性              。

解释:

Point3D的成员至少与Point相同      ,则Point兼容Point3D   。 所以                    ,成员多的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) => {})

解释:

参数少的可以赋值给参数多的              ,所以                   ,f1可以赋值给f2                    。 数组forEach方法的第一个参数是回调函数      ,该实例中类型为:(value: string, index: number, array: string[])=>void       。 在JS中省略用不到的函数参数实际上是很常见的          ,这样的使用方式                    ,促成了TS中函数类型之间的兼容性         。 并且因为回调函数是有类型的         ,所以      ,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

解释:

注意               ,此处与前面讲到的接口兼容性冲突                  。 技巧:将对象拆开,把每个属性看做一个个参数                 ,则                  ,参数少的(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

解释:

如果返回值类型是原始类型                   ,此时两个类型要相同      ,比如          ,左侧类型F5和F6   。 如果返回值类型是对象类型                    ,此时成员多的可以赋值给成员少的         ,比如      ,右侧类型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 }

解释:

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

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

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

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

解释:

MyArray接口模拟原生的数组接口,并使用[n: number]来作为索引签名类型                  。 该索引签名类型表示:只要是number类型的键(索引)都可以出现在数组中                 ,或者说数组中可以有任意多个元素                 。 同时也符合数组索引是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 }

解释:

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

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

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

解释:

首先               ,先执行keyof Props获取到对象类型Props中所有键的联合类型即,‘a’ | ‘b’ | ‘c’                   。 然后                 ,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>

解释:

keyof Tkeyof Props表示获取Props的所有键                   ,也就是:‘a’ | ‘b’ | ‘c’                  。 在[]后面添加?(问号)      ,表示将这些属性变为可选的          ,以此来实现Partial的功能              。 冒号后面的T[P]表示获取T中每个键对应的类型   。比如                    ,如果是’a’则类型是number;如果是’b’则类型是string                  。 最终         ,新类型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}{评论                    ,你的意见是我进步的财富!}

评论            ,你的意见是我进步的财富!
声明:本站所有文章   ,如无特殊说明或标注                    ,均为本站原创发布                   。任何个人或组织               ,在未征得本站同意时,禁止复制            、盗用   、采集                    、发布本站内容到任何网站               、书籍等各类媒体平台           。如若本站内容侵犯了原著者的合法权益                 ,可联系我们进行处理      。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
帝国cms插件编写教程下载(帝国CMS列表页面调用关键字TAG的方法) 营销型企业网站建设方案中,应该考虑哪些因素的选择(企业营销型网站设计)