泛型的基本使用
// 创建一个泛型函数
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[ ]。