TypeScript高级类型
1、泛型 和 keyof
泛型是可以在保证类型安全(不丢失类型信息)前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class 中。
泛型的基本使用
泛型我理解为:是一个动态的类型变量、通过泛型传递不同的类型、提高复用性
//创建泛型函数
function ID<Type>(value:Type):Type{
return value
}
//调用泛型函数
const num= ID<number>(12)//以number类型调用泛型函数
const str= ID<string>('233')//以string类型调用泛型函数
解释:
1. 语法:在函数名称的后面添加 <>(尖括号),尖括号中指定具体的类型,比如,此处的 number。
2. 当传入类型 number 后,这个类型就会被函数声明时指定的类型变量 Type 捕获到。
3. 此时,Type 的类型就是 number,所以,函数 id 参数和返回值的类型也都是 number。 同样,如果传入类型 string,函数 id 参数和返回值的类型就都是 string。 这样,通过泛型就做到了让 id 函数与多种不同的类型一起工作,实现了复用的同时保证了类型安全。
泛型函数的简化调用
在调用泛型函数时,根据类型推断的机制、可以省略 <类型> 来简化泛型函数的调用。
//创建泛型函数
function ID<Type>(value:Type):Type{
return value
}
let num1=ID(12)
let str1=ID('24')
推荐:使用这种简化的方式调用泛型函数,使代码更短,更易于阅读。
泛型约束
添加泛型约束收缩类型,两种方式:1 指定更加具体的类型 2 添加约束。
1.指定更加具体的类型
function ID<Type>(value:Type[]):Type[]{
value.length
return value
}
将类型修改为 Type[](Type 类型的数组),
因为只要是数组就一定存在 length 属性,因此就可以访问了。
2.添加约束
通过 extends 关键字使用该接口,为泛型(类型变量)添加约束。
interface ILength{ length:number}
// Type类型需要满足ILength中的要求:传入的参数要有length属性
function id<Type extends ILength >(value:Type):Type{
return value
}
id({length:2,name:'李hs'})
id(['1','2'])
id('2')
//错误
//id(1)
注意:传入的实参(比如,数组)只要有 length 属性即可,
这也符合前面讲到的接口的类型兼容性。
3.添加约束使用keyof
泛型的类型变量可以有多个,并且类型变量之间还可以约束
keyof 关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型。
比如,创建一个函数来获取对象中属性的值:
//extends keyof 声明 key值必须是Type类型中有的属性
function getProp<Type,key extends keyof Type>(obj:Type,key:key){
return obj[key]
}
getProp({name:'李寒松',age:18},'age')
getProp({name:'李寒松',age:18},'name')
//错误演示
//getProp({name:'李寒松',age:18},'name1')
类型变量 Key 受 Type 约束、只能访问对象中存在的属性。
泛型接口
泛型接口:接口也可以配合泛型来使用,以增加其灵活性,增强其复用性。
interface IdFunc<Type>{
id:(value:number)=>Type
ids:()=>Type[]
}
let obj:IdFunc<number>={
id:(value)=>value,
ids:()=>[1,3,5]
}
obj.id(22)
1. 在接口名称的后面添加 <类型变量>,那么,这个接口就变成了泛型接口。
2. 接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量。
3. 使用泛型接口时,需要显式指定具体的类型(比如,此处的 IdFunc<nunber>)。
泛型接口扩展
JS 中的数组在 TS 中就是一个泛型接口。
const strs=['a','b','c']
strs.forEach(item=>{})
const nums=[1,2,3]
nums.forEach(item=>{})
解释:当我们在使用数组时,TS 会根据数组的不同类型,来自动将类型变量设置为相应的类型。
技巧:可以通过 Ctrl + 鼠标左键(Mac:option + 鼠标左键)来查看具体的类型信息。
泛型类
类似于泛型接口,在 class 名称后面添加 <类型变量>,这个类就变成了泛型类。
创建泛型类
//创建泛型类
class GenericNumber<NumType>{
defaultValue:NumType
add:(x:NumType,y:NumType)=>NumType
}
//明确指定<类型>
const myNum=new GenericNumber<number>()
myNum.defaultValue=1
泛型工具类型
泛型工具类型:TS 内置了一些常用的工具类型,来简化 TS 中的一些常见操作。
说明:它们都是基于泛型实现的(泛型适用于多种类型,更加通用),并且是内置的,可以直接使用。
1.工具类型 Partial
Partial 用来构造(创建)一个类型,将 Type 的所有属性设置为可选。
interface Props{
id:string
children:number[]
}
//Partial<Props>对象中的参数是可选的
type partialProps=Partial<Props>
let p2:partialProps={id:'001'}
解释:构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的。
2.工具类型 Readonly
Readonly 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)。
interface Props{
id:string
children:number[]
}
type ReadonlyProps=Readonly<Props>
let p1:ReadonlyProps={
id:'1',
children:[1,3]
}
//p1.id='22'//无法分配到 "id" ,因为它是只读属性。
3.工具类型 Pick<Type,Keys>
Pick<Type, Keys> 从 Type 中选择一组属性来构造新类型。
interface Props{
id:string
children:number[],
title:string
}
//拷贝Props中的属性形成新的类型
//传入的属性必须是Props中有的属性
type PickProps=Pick<Props,'id'|'children'>
4.工具类型 Record<Keys,Type>
Record<Keys,Type> 构造一个对象类型,属性键为 Keys,属性类型为 Type。
type RecordObj=Record<'a'|'b'|'c',number[]>
let obj:RecordObj={
a:[1,3,5],
b:[2,4,6],
c:[3,5,7]
}
1. Record 工具类型有两个类型变量:1 表示对象有哪些属性 2 表示对象属性的类型。
2. 要创建的属性的类型是相同的可以使用Record工具类
2、索引签名类型
绝大多数情况下,我们都可以在使用对象前就确定对象的结构,并为对象添加准确的类型。
使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了。
对象
前置知识:JS 中对象({})的键是 string 类型的。
interface AnyObject{
[key:string]:number
}
let obj:AnyObject={
a:2,
abc:1,
abcd:1245
}
数组
数组的键(索引)是数值类型。
//数组
const arr=[1,3,5]
arr.forEach
interface myArr<Type>{
[index:number]:Type
}
let arr1:myArr<number>=[1,2,4]
arr1[0]
3、映射类型
映射类型:基于旧类型创建新类型(对象类型),减少重复、提升开发效率。
根据联合类型创建新类型
//传统写法
type PropKeys='x'|'y'|'z'
type Type1={ x:number; y:number;z:number}
//映射类型
type type2={[key in PropKeys]:number}
注意:映射类型只能在类型别名中使用,不能在接口中使用。
根据对象类型创建新类型
type Proos={ a:number;b:string;c:boolean}
//拿到所有键
type Type={ [key in keyof Proos]:number}
Partial是基于映射实现的
泛型工具类型(比如,Partial)都是基于映射类型实现的。
interface Props{id:string,children:number[]}
type partialProps=Partial<Props>
// type Partial<T> = {
// [P in keyof T]?: T[P];
// };
解释:
1. keyof T 即 keyof Props 表示获取 Props 的所有键,也就是:'a' | 'b' | 'c'。
2. 在 [] 后面添加 ?(问号),表示将这些属性变为可选的,以此来实现 Partial 的功能。
3. 冒号后面的 T[P] 表示获取 T 中每个键对应的类型。比如,如果是 'a' 则类型是 number;如果是 'b' 则类型是 string。
4. 最终,新类型 PartialProps 和旧类型 Props 结构完全相同,只是让所有类型都变为可选了。
5.
4、索引查询类型 T[P]
T[P] 语法, 在 TS 中叫做索引查询(访问)类型
作用:用来查询属性的类型。
索引类型基本使用
type Props={a:number;b:number;c:number}
type TypeA=Props['a']
//索引查询类型模拟Partial类型
type MyPartial<T>={ [P in keyof T]?:T[P]}
type PartialProps=MyPartial<Props>
type TypeB=PartialProps['a']
解释:Props['a'] 表示查询类型 Props 中属性 'a' 对应的类型 number。所以,TypeA 的类型为 number。
注意:[] 中的属性必须存在于被查询类型中,否则就会报错。
索引类型查询多个
type Props={ a:number;b:string;c:boolean}
//字面量联合类型获取a、b的类型
type TypeA=Props['a'|'b'] //string | number
//keyof获取Props中所有的类型
type TypeB=Props[keyof Props] //string | number | boolean