类型推断之 infer 推断 infer 关键字 只能用在条件类型中 用来提取类型中每一部分的类型 放在不同的位置 提取不同的类型
推导函数返回类型
function getUser(name:string,age:number){
return {name,age,address:{}
}
typeof getUser;// 像这样根本不知道获取的是啥
可以这么改
首先 需要设置判断T extends 一个函数
T extends functtion// 离谱
T extends Function// extends 包装器 更离谱
T extends ()=>{} // 一个函数例子 ,没有返回更 参数更离谱
所以先这样 表示extends 继承一个函数 返回任意值
type ReturnType<T> =T extends (...arg:any[])=any? any:never;
再这样 表示吧any 改成R 的占位符 对返回值R 进行推导有R返回R,没R返回never
type ReturnType<T> =T extends (...arg:any[])=>infer R? R:never;//T 满足当前函数的条件 根据infer的位置推断值的类型 并且返回类型
type T1=ReturnType <typeof getUser>
换成推导参数类型
首先 T得满足 (...args:any[])=>any
type Parameters<T extends (...args:any[])=>any>=//这里的等号后面给的事判断条件
T extends(...args:any[])=>any?any:nerver;//我们现在要约束参数了
type Parameters<T extends (...args:any[])=>any>=
T extends(...args:infer P)=>any?P:never;
返回实例类型判断
type InstanceType< T extends new(...args:any[])=>any>=T extends {new (...args:any[]):infer R;}?R:never
构造函数参数判断
type ConstructorParameters< T extends new(...args:any[])=>any>=T extends {new (...args:infer R):any;}?R:never
type TailToHead<T extends any[]>= T extends [infer A,....infer C ,inferB ]?
[A,B,C]:any;
// 元组位置调整
type x=TailToHead<['cxk',1,2,3,'dfsdf']>
//数组抽离联合类型
type ElementOf<T> =T extends Array<infer R>?R:any;
type TupleToUnion =ElementOf<[string,number,boolean]>
对象操作(隐射类型)
type A={name:string}
type B={age:number}
type Compute<T extends objcet>={
[K in keyof T]:T[K];
}
type AB=Compute<A&B>
interface Company{
num:number,
name:string
}
inferface Person<T>{
name:string;
age:number;
company:T;
}
//可选
type Partial<T>={
[K in keyof T]?:T[K];
}
//深度可选
type DeepPartial<T>={
[K in keyof T]?:T[K] extends object ?DeepPartial<T[K]>:T[K];
}
//深度必填
type DeepRequired<T>={
[K in keyof T]-?:T[K] extends object ?DeepRequired<T[K]>:T[K];
}
type witchCompany =Partial<Person<Company>>
Pick(挑选某些) Omit(剔除某些)
type Pick<T, K extends keyof T>={
[P in K]:T[P]
};
type Omit<T,K extends keyof any>=Pick<T,Exclude<keyof T, K>>
type PickPerson=Pick<Person,'name'|'age'>
Record 为了更好的描述对象类型 例如
let obj :Record<string,any>={name:'cxk',age:28};
function map <K extends string,V,R>(
obj:Record<K,V>,
callback:(item:V,key:K)=>R
){
let result={} as Record<K,R>
for(let key in obj){
result[key]=callback(obj[key],key)
}
return result
}
map<'name'|'age',string|number,number>({name:'cxk',age:30},(item,key)=>{
return
})
type Record<K extends keyof any, T>={[P in K]: T}
ts 兼容性类型推导
ts兼容性 从结构上 从类型上 值类型兼容 ts主要考虑的是安全,只要是安全的可以赋值
函数兼容性
参数少的是子类型 返回值要求安全,返回值要求是子类型
let sum1=(a:number,b:number):number|string=>a+b
let sum2=(a:number):number=>a;
type Sum1=typeof sum1;
type Sum2 typeof sum2
type X= Sum2 extends Sum1?true:false//true
const forEach=<T>(arr:T[],callback:(val:T,key:number)=>string|number)=>{
for(let i=0;i<arr.length;i++){
let r=callback(arr[i],i)
}
}
forEach(['A',2,3,{}],function(val){
return "cxk"
})
类也有兼容性 比较的是实例
class A{}
calss B{}
const b:B=new A()//这里都是空的所以能赋值
class A{
cxk=1
}
calss B{
cxk=1
}
const b:B=new A()//这里返回值一致所以能赋值
但是类中有private或者protected两个值不能互相赋值
类有 结构类型 也有标称类型
type BTC=number
type USDT=number
const c1:BTC=100
const c2:USDT=100
上述都是number类型不够区分
改造一下
方案①
type withType<T,K>=T&{type:K}//这么搞 下面都是btc 但是还会产生区分 这时候可以用class
type BTC=withType<number,'BTC'>
type USDT=withType<number,'BTC'>
再改一下
方案②
class AddType<K>{
private type!: K
}
type withType<T,K>=T&AddType<K>
type BTC=withType<number,'BTC'>
type USDT=withType<number,'BTC'>
如果想简单一点可以用方案①
const c1=100 as BTC
const c2 100 as USDT
逆变协变都是基于函数的 逆变:在函数参数可以标记儿子传父亲
协变:可以标记父亲返回儿子
class Parent{
car(){}
}
class Child extends Parent{
house(){}
}
class Grandson extends Child{
sleep(){}
}
//这里传
function fn(callback:(ctr:Child)=>Child){
callback(new Child());
}
fn((child:Parent):Child=>{ //这里标记的是父亲返回的是儿子
return new Child()
})
从安全性考虑 内部调用函数的时候 可以少参数赋值给多参数的 这就是逆变从参数上来说
函数返回值 需要返回子类 因为内部代码在访问内属性的时候需要保证能安全的访问到 这是协变
ingerface MyArray<T>{
concat:(...args:T[])=>T[];
concat:(...args:T[]):T[];这种写法不会进行逆变检测,所以描述对象中方式时候采用这种,慎用
}
类型层级兼容性:
never->字面量->基础类型->包装类型->any/unknown