学习TypeScrip的第四天

泛型:

泛型是可以在保证类型安全的前提下,让函数等与多种类型一起工作,从而实现复用,常用于函数,接口,class中

创建泛型函数:

function id<Type>(value: Type): Type {
    return value
}

(1)语法:在函数名称后面添加<>(尖括号),尖括号添加类型变量,比如此处的Type

(2)类型变量Type,是一种特殊类型的变量,它处理类型而不是值

(3)该类型变量相当于一个容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数的时候指定)

(4)因为Type是类型,因此可以将其作为函数参数的返回值类型,表示参数和返回值具有相同的类型

(5)类型变量是Type,可以是任意合法的变量名称

调用泛型函数:

function id<Type>(value: Type): Type {
    return value
}
// 调用
const num = id<number>(10)
const str = id<string>('hello')

语法:函数名称后面添加<>(尖括号),尖括号指定的具体类型,比如此处的number或string,

这样,通过泛型就做到了让id函数与多种不同类型一起工作,实现了复用的同时保证了类型安全

简化调用泛型函数:

(1)在调用泛型函数的时候,可以省略<类型>来简化泛型函数的调用

(2)此时,TS内部会采用一种叫做类型参数推断的机制,来根据传入的实参自动推断出变量Type的类型

推荐:使用这种简化的方式调用泛型函数,使代码更短,更易于阅读

但是,当编译器无法推断类型或者推断的类型不准确时,就需要显示的传入参数类型

泛型约束:

默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性

比如:下面这个代码获取参数的长度:
 

function id<Type>(value: Type): Type {
    // value.length会报错
    console.log(value.length)
    return value
}

报错是因为Type代表的是任意类型,无法保证一定存在length属性,比如number类型就没有length;

所以,此时,就需要为泛型添加约束来收缩类型(缩窄类型的取值范围)。

添加类型约束收缩类型,主要有两种方式:

1、指定更加具体的内容

interface ILength {length: number}
function id<Type>(value: Type[]): Type[] {
    console.log(value.length)
    return value
}

上面这个代码就是将类型修改为Type[] (Type类型的数组),因为只要是数组就有length属性,就可以访问了

2、添加约束

interface ILength {length: number}
function id<Type extends ILength>(value: Type): Type {
    console.log(value.length)
    return value
}

(1)创建描述约束的接口ILength,该接口提供length属性

(2)通过extends关键字使用该接口,为泛型(类型变量)添加约束(这里的extends不是代表继承,而是约束)

(3)该约束表示:传入的类型必须具有length属性

注意:传入的实参(比如数组)只要有length属性即可,这也符合前面讲到的接口的类型兼容

泛型类型可以有多个,并且类型变量之间还可以约束:(比如:第二个类型变量受第一个类型变量的约束)

比如:创建一个函数来获取对象中属性的值

function getProps<Type, Key extends keyof Type>(obj: Type, key: Key): {
    return obj[key]
}
let person = {name: 'jack', age: 18}
getProps(person, 'name')
getProps(person, 'age')

(1)添加了第二个类型变量Key,两个类型变量之间使用,(逗号)分隔

(2)keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型

(3)这个例子中,keyof Type实际上获取的是person对象所有的键的联合类型,也就是 'name' | 'age'

(4)类型变量Key受Type约束,可以理解为: key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性

泛型接口:
接口可以配合泛型来使用,以增加其灵活性,增强其复用性

// 泛型接口
interface IdFunc<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}
let obj: IdFunc<number> = {
  id(value){return value},
  ids(){return [1,2,4]}
}

(1)在接口名称的后面添加<类型变量>,那么,这个接口就变成了泛型接口

(2)接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量

(3)使用泛型接口时,需要显示指定具体的内容

(4)上面的例子中,id方法的参数和返回值类型都是number,ids方法的返回值类型是number[]

实际上,JS中的数组在TS中就是一个泛型接口

因为当我们在使用数组时,TS会根据数组的不同类型,来自动的将类型变量设置为相应的类型

技巧:可以通过Ctrl+鼠标左键来查看具体的类型信息

泛型类:

// 泛型类
class GenericNumber<NumType> {
  defaultValue: NumType
  add: (x: NumType, y: NumType) => NumType
}
// 当其构造函数里指定了的话
// 下面那个new GenericNumber<number>()可以不用<number>
const myNum = new GenericNumber<number>()
muyum.defaultValue = 10

// 就比如
class GenericNumber1<NumType> {
  defaultValue: NumType
  add: (x: NumType, y: NumType) => NumType
  constructor(value: NumType){
    this.defaultValue = value
  }
}
// 此时 可以省略类型变量不写
const myNum1 = new GenericNumber1(100)
myNum1.defaultValue = 10

泛型工具类:

TS内置了一些常用的工具类型,来简化TS中一些常见操作

它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且都是内置的,可以直接在代码中使用,有很多的类型,主要学习:

1、Partial<Type>   2、Readonly<Type>   3、Pick<Type>   4、Record<Type>

1、Partial<Type>用来构造(创建)一个类型,将Type的所有属性设置为可选(并不会改变传入的类型本身)

interface Props {
    id: string
    children: number[]
}
const ParticalProps = Partial<Porps>

构造出来的新类型ParticalProps结构与Props相同,但所有属性都变为可选的

2、Readonly<Type>用来构造一个类型,将Type的所有属性都设置为readonly(只读)

interface Props {
    id: string
    children: number[]
}
type ReadonlyProps = Readonly<Props>
let props: ReadonlyProps  = {
    id: '1',
    children: []
}
// 下面这一行代码会报错,因为是只读的不能改
props.id = '2'

3、Pick<Type, Keys>从Type中选择一组属性来构造新类型

(1)有两个类型变量,Type是表示选择谁的属性,Keys表示选择哪几个属性

(2)其中第二个类型变量,如果只选一个则只传入该属性名即可

(3)第二个类型变量传入的属性只能是第一个类型变量中存在的属性

interface Props {
    id: string
    title: string
    children: number[]
}
type PickProps = Pick<Props, 'id' | 'title'>
// 构造出来的PickProps只有id和title两个类型属性

4、Record<Keys, Type>构造一个类型对象,属性键为Keys,属性类型为Type

第一个参数表示对象有哪些属性,第二个参数表示对象属性的类型

type RecordObj = Record<'a' | 'b' | 'c', string[]>
let obj: RecordObj = {
    a: ['a'], b: ['b'], c: ['c']
}

索引签名类型:

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

但是:当无法确定对象中有哪些属性(或者说对象中可以出现任意多属性)就要用到索引签名类型

// 索引签名类型
interface AnyObject {
  [key: string]: number
}
let obj: AnyObject = {
    a: 1
    b: 2
}

(1)使用[key: string]来约束该接口中允许出现的属性名称,表示只要是string类型的属性名称,都可以出现在对象中

(2)这样,对象obj中就可以出现任意多个属性(比如:a,b等)

(3)key只是一个占位符,可以换成任意合法的变量名称

(4)前置知识:JS中对象({})的键是string类型的

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

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

模拟数组类型:

// 模拟原生的数组接口,并使用 [n: number] 来作为索引类型签名
interface myArray<T> {
// 只要是number类型的键(索引)都可以出现在数组中
// 或者说数组中可以有任意多元素
    [n: number]: T
}
let arr: myArray<number> = [1, 3, 5]

映射类型:

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

比如:类型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 Props = 'x' | 'y' | 'z'
tyep Type2 = {[key in Props]: number}

key in Props表示key可以是Props联合类型中的任意一个,类似于forin(let key in obj)

使用映射类型创建的新对象类型(Type)和类型Type1结构完全相同

映射类型只能在类型别名中使用,不能在接口中使用

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

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

(1)首先,先执行keyof Props获取到对象类型Props中所有的键的联合类型 'a' | 'b' | 'c'

(2)然后,key in ... 就表示key可以是Props中所有的键名称中的任意一个

索引查询类型:

T[P] 的语法 ---> 用来查询属性的类型

type Props = {a: number; b: string; c: boolean}
type TypeA = Props['a']
// 一定要写type TypeA,不然会报错
// 鼠标放在TypeA上面会显示type TypeA = number

Props['a']表示查询类型Props中属性'a'对应的类型number,所以TypeA的类型为number

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

同时查询多个索引类型:

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

使用keyof操作符获取Props中所有键对应的类型

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值