TS的泛型(取自黑马)

泛型的基本使用

// 创建一个泛型函数
function id<Type>(value: Type): Type { return value }

//调用泛型函数(举个例子)
// 1.以number 类型调用泛型函数
const num = id<number>(10)
// 2.string 类型调用泛型函数
const num1 = id<string>('知更鸟')

//使用箭头函数来表示泛型
const fn = <Type>(a: Type): Type => { 		return a; 	};
const num2 = fn<number>(10)

解释:

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

2.当传入类型 number后,这个类型就会被函数声明时指定的类型变量Type 捕获到。

3.此时,Type 的类型就是number,所以,函数id 参数和返回值的类型也都是 number。

同样,如果传入类型 string,函数id 参数和返回值的类型就都是string。这样,通过泛型就做到了让id 函数与多种不同的类型一起工作,实现了复用的同时保证了类型安全

简化调用泛型函数:

// 创建一个泛型函数
function id<Type>(value: Type): Type { return value }

//简化调用泛型函数(举个例子)
// 1.以number 类型调用泛型函数
// const num = id<number>(10)
const num = id(10)
// 2.string 类型调用泛型函数
// const num1 = id<string>('知更鸟')
const num1 = id('知更鸟')

解释:

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

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

3.比如,传入实参10,TS 会自动推断出变量num的类型 number,并作为 Type 的类型。

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

说明:当编译器无法推断类型或者推断的类型不准确时,就需要显式地传入类型参数。

泛型约束:

默认情况下,泛型函数的类型变量Type 可以代表多个类型,这导致无法访问任何属性。比如,id('a')调用函数时获取参数的长度:

function id<Type>(value:Type):Type{
console.log(value.length); //报错 类型Type上不存在属性'length'
return value
}

解释:

Type 可以代表任意类型,无法保证一定存在length属性,比如number 类型就没有length。
此时,就需要为泛型添加约束来收缩类型(缩窄类型取值范围)。

添加泛型约束收缩类型:

主要有以下两种方式:1指定更加具体的类型 2添加约束

1.指定更加具体的类型:

function id<Type>(value: Type[]): Type[] {
  //加上了[],现在value可以运用数组方法
  console.log(value.length);
  return value
}

比如,将类型修改为Typell(Type类型的数组),因为只要是数组就一定存在length属性,因此就可以访问了。

 2.添加约束解释:

interface ILength {
  length: number
}

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

id(['a','c'])
id('abc')
id({length:10,name:'name'})

解释:

1.创建描述约束的接口length,该接口要求提供length 属性

2.通过 extends 关键字使用该接口,为泛型(类型变量)添加约束。

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

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

这里不一定要用接口,说白了,就是需要一个对象类型,你直接把一个对象类型放在extends后面也可以,要变通

多个泛型的类型变量的情况

泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如,第二个类型变量受第一个类型变量约束)比如,创建一个函数来获取对象中属性的值:

function getProp<Type, Key extends keyof Type>(obj: Type, Key: Key) {
  return obj[Key]
}

getProp({ name: 'jack', age: 18 }, 'age')
getProp({ name: 'jack', age: 18 }, 'name')

getProp(18, 'toFixed')
getProp('abc', 'split')
getProp('abc', 1) //此处 1 表示索引
getProp(['a'], 'length')
getProp(['a'], 1000)

console.log('object'[1]);  //b

//错误演示
//getProp({name:'jack',age:'18'},'name1') Key不能是没有的属性

解释:

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

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

3.本示例中 keyofType 实际上获取的是person 对象所有键的联合类型,也就是:'name''age'。

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

泛型接口

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

interface IdFun<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}

let obj: IdFun<number> = {
  id(value) { return value },
  ids() {
    return [1, 3, 5]
  },
}

解释:

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

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

3.使用泛型接口时,需要显式指定具体的类型(比如,此处的IdFunc<nunber>)

4.此时,id方法的参数和返回值类型都是number; ids方法的返回值类型是number[ ]。

 

泛型类

创建泛型类

// class GenericNumber<NumType>{
//   defaultValue:NumType
//   add:(x:NumType,y:NumType)=>NumType

//   constructor(value:NumType){
//     this.defaultValue=value
//   }
// }
// //此时可以省略<类型> 不写
// const myNum=new GenericNumber(100)
// myNum.defaultValue=10

class GenericNumber<NumType> {
  defaultValue: NumType
  add: (x: NumType, y: NumType) => NumType
}
//这种情况下,推荐明确指定<类型>
const myNum = new GenericNumber<number>()
myNum.defaultValue = 10

解释:

1.类似于泛型接口,在class 名称后面添加<类型变量>,这个类就变成了泛型类

2.此处的 add 方法,采用的是箭头函数形式的类型书写方式。

类似于泛型接口,在创建 class 实例时,在类名后面通过<类型>来指定明确的类型

如果说泛型类不用我们去明确指出类型的话,那就可以进行省略(比如构造函数)

 

 泛型工具类型

Partial(可选属性)

泛型工具类型 - Partial<Type>用来构造(创建)一个类型,将Type的所有属性设置为可选

interface Props {
  id: string
  children: number[]
}
type PartialProps = Partial<Props>

let p1: Props = {
  id: '',
  children: [1]
}
//可选属性
let p2: PartialProps = {
  id: '',
  //children:[]
}

解释: 构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的

 Readonly(只读属性)

泛型工具类型 - Readonly<Type>用来构造一个类型将Type的所有属性都设置为readonly(只读)。

interface Props{
  id:string
  children:number[]
}
type ReadonlyProps=Readonly<Props>
let props:ReadonlyProps={id:'1',children:[]}
props.id='2' //当我们想重新给id 属性赋值时,就会报错:无法分配到"id",因为它是只读属性。

解释:

构造出来的新类型 ReadonlyProps结构和 Props 相同,但所有属性都变为只读的。

当我们想重新给id 属性赋值时,就会报错:无法分配到"id",因为它是只读属性。

 

 Pick(选择一组属性来构造新类型)

泛型工具类型-Pick<Type,Keys>从 Type 中选择一组属性来构造新类型

interface Props {
  id: string
  title: string
  children: number
}
type PickProps = Pick<Props, 'id' | 'title'>
let ps:PickProps={
  id:'1',
  title:'2'
}

解释:

1.Pick 工具类型有两个类型变量:1表示选择谁的属性2表示选择哪几个属性。

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

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

4.构造出来的新类型 PickProps,只有id 和 title 两个属性类型。

 

Record(构造一个对象类型)

泛型工具类型 - Record<Keys,Type>构造一个对象类型,属性键为Keys,属性类型为 Type。

type RecordObj = Record<'a' | 'b' | 'c', string[]>

// type RecordObj = {
//   a: string[]
//   b: string[]
//   c: string[]
// }
//上面两种方法都是可以的,前者明显方便一些
let objs: RecordObj = {
  a: ['1'],
  b: ['2'],
  c: ['3']
}

 解释:

1.Record 工具类型有两个类型变量: 1表示对象有哪些属性  2表示对象属性的类型。

2.构建的新对象类型 RecordObj表示:这个对象有三个属性分别为a/b/c,属性值的类型都是string[ ]。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值