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

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

时间2025-04-28 15:45:55分类IT科技浏览3470
导读:前言...

前言

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

专栏链接👉🏻【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
vue全局组件定义在哪(vue3 自定义全局loading组件 (PC和移动端都非常适用)) 新网站多久收录(做SEO新站如何实现当天收录?)