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

这篇具有很好参考价值的文章主要介绍了【前端进阶】-TypeScript高级类型 | 交叉类型、索引签名类型、映射类型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

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

类型兼容性

两种类型系统:

  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中所有的键名称中的任意一个。

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


分析泛型工具类型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}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!
文章来源地址https://www.toymoban.com/news/detail-402515.html

到了这里,关于【前端进阶】-TypeScript高级类型 | 交叉类型、索引签名类型、映射类型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端TypeScript学习day03-TS高级类型

    (创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)            目录 TypeScript 高级类型 class 类 class继承  extends implements  类成员可见性  public  protected  private   readonly 兼容性 类型兼容性 接口兼容性  函数兼容性  TypeScri

    2024年02月08日
    浏览(37)
  • Typescript - 索引签名

    在 TypeScript 中,索引签名是一种定义对象类型的方式,它允许我们使用字符串或数字作为索引来访问对象的属性。 索引签名通过以下语法进行定义: 或者 其中, index 是指定索引的名称,可以是 string 或 number 类型; type 则表示索引对应的值的类型。 索引签名的作用是允许我

    2024年02月13日
    浏览(42)
  • typescript & 交叉类型

    TypeScript中的交叉类型是指将多个类型合并为一个类型。这使得我们可以将现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 写这篇文章先问大家一个问题,如何让一个对象既有a类型约束,又有b类型约束? 如果你看了我这篇文章typescrip接口 interface详解

    2024年02月07日
    浏览(37)
  • TypeScript 学习笔记(一):基本类型、交叉类型、联合类型、类型断言

    TS中实现对象属性必选、对象属性在开发过程中十分常见,前端在传参数时,有些参数比必传,有些是选传,我们可以定一个多个对象来实现传参,但是这让代码变得冗余。我们可以通过TS定义数据类型来实现。 TypeScript中文网 1. 数组 2. 布尔 3. 数值 当我们给num赋值为123但没有

    2024年02月15日
    浏览(45)
  • 高级的E2EE——交叉签名(区块链密码签名)(第一篇-SAS)

      如果你使用了上篇文章技术,在客户端中成功实现了端到端加密。那么恭喜你!这是真正值得骄傲的一步,也是值得庆祝的一步!但是,您可能已经注意到,有些事情仍处于粗糙边缘:您必须通过比较公钥来手动验证所有其他设备,解密密钥不会在您的设备之间共享等。如

    2024年01月19日
    浏览(45)
  • TypeScript中的对象类型(可选属性 只读属性 交叉类型)

    一、定义对象类型 在TypeScript中定义对象类型有以下三种方式: 1. 匿名对象类型 匿名对象类型是在定义变量时直接使用花括号{},来定义一个对象类型。例如: 上述代码中定义了一个person变量,它的类型为对象,它有两个属性:name和age,其中name属性的类型为字符串,age属性

    2024年02月11日
    浏览(49)
  • TypeScript中的keyof、typeof、索引访问类型、条件类型

    TypeScript中的keyof类型操作符 可以获取某个类型的所有属性名组成的联合类型 。这个操作符的作用是帮助开发者在静态类型检查中更准确地操作属性名。 举例来说,如果我们有如下一个接口: 我们可以使用keyof来获取这个接口的属性名联合类型: 有了属性名联合类型,我们可

    2024年02月11日
    浏览(52)
  • TypeScript之高级类型

    除了 string 、 number 、 boolean  这种基础类型外,在  typescript  类型声明中还存在一些高级的类型应用 这些高级类型,是 typescript 为了保证语言的灵活性,所使用的一些语言特性。这些特性有助于我们应对复杂多变的开发场景 常见的高级类型有如下: 交叉类型 联合类型 类型

    2024年02月06日
    浏览(41)
  • TypeScript基础知识:高级数据类型

            TypeScript 是一种静态类型检查的 JavaScript 超集,它提供了许多强大的高级数据类型,可以帮助我们更好地定义和操作数据。本文将深入介绍 TypeScript 的高级数据类型,包括交叉类型、联合类型和映射类型,并通过示例代码演示它们的用法和优势。         交叉

    2024年01月17日
    浏览(51)
  • 两小时快速入门 TypeScript 基础(一)工作流、基本类型、高级类型

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,也会涉及到服务端(Node.js 等) 📃 个人状态: 2023届本科毕业生,已拿多个前端 offer(秋招) 🚀 未来打算: 为中国的工业软件事业效力 n 年 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/

    2024年02月11日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包