ts学习总结6

83 阅读3分钟

类型推断之 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