ts泛型复习
function valueProjectorReturnSameType<T>(arg1:T):T{
return arg1
}
function valueProjectorReturnSameType2<T,G>(arg1:T|G):T|G{
return arg1
}
函数名<T>指定使用泛型T
泛型,但要求有某个属性,要用比如T.name? extends泛型约束
interface SomeType{
name:string
}
function genericTypeWithRestraint<T extends SomeType>(arg1:T){
return arg1.name
}
console.log(genericTypeWithRestraint({name:'wang',age:10}))
函数名<T extends 一个接口> 添加约束
类型推断练习
套路:type TargetType<T> = T extends 问题类型,要推理的类型使用infer ? K:T
1.写一个PromiseType类型能够获取Promise<string>里的string
type PromiseType<T> = ? // T extends Promise<infer K>?K:T
type MyPromiseReturnType = PromiseType<Promise<string>>
const a:MyPromiseReturnType = 'a'
有一个类型组成的数组[string, number],写一个类型推断出该数组元素的类型
type ArrayTypeList<T> = ?? // T extends (infer I)[]?I: T
type ItemType1 = ArrayTypeList<[string,number]>
const item1:ItemType1='a' // string|number
- 函数有关的推断
\\推断第一个参数的类型
type FirstArg<T> = T extends (first: infer F, ...args:any[])=>any?F:T;
type FA = FirstArg<(name:string,age:number)=>void>
type PersonHere = {
person:string
}
type MyReturnType<T> = T extends (name:string,age:number)=>infer F?F:T;
type ReturnedType = MyReturnType<(name:string,age:number)=>PersonHere>
const mannn:ReturnedType = {person:'aaa'}
协变(Covariance) 与 逆变(Contravariance)
如果已有Animal的子类Dog,
现在用一个TypeConverter将这两个类分别转化成新类型:
- 类型Dog+:TypeConverter\<Dog\>
- 类型Animal+:TypeConverter\<Animal\>
如果依旧 Dog+ 可以赋值给Animal+, 即Dog+依旧是Animal+的子类,那么此时TypeConverter发生的是协变,因为它和变化前的亲子关系一致。反之,则是协变。
这种typeconverter可能是Array(),显然Array是协变; typeconverter也可能是函数,但如果是形参,则使用父类型作为形参的函数,可以安全赋值给子类型作为形参的函数,而反过来不行,此时父子关系反过来了,发生了逆变;如果是返回值,那么还是协变。
举个例子:
interface Person{
name:string
}
interface Programmer extends Person{
skill:string
}
type pf=(arg:Person)=>void //定义一个pf类型的函数
type tf=(arg:Programmer)=>void //定义一个tf类型的函数
let f1:pf=(c:Person)=>{//用了Person的类方法}
let f2:tf=(p:Programmer)=>{//用了Programmer的类方法skill}
f1=f2 //error! skill is missing in pf
//f1是pf类型,他想要接收一个Person类型的参数,
//如果调用f1,因为f1是pf类型,所以调用时传进去一个Person类型的变量,
//但函数体变成了f2,它是对Programmer处理的,比如可能使用c.skill,而Person类型的变量没有skill。
//于是造成了类型不安全
f2=f1 //ok. f2变成f1后,因为f1接受子类对象o,而调用f2时,使用子类对象,以及f1中定义的逻辑,这个o一定都能用
返回值时:
interface Person{
name:string
}
interface Programmer extends Person{
skill:string
}
type pf=()=>Person
type tf=()=>Programmer
let f1:pf=()=>({name:'wang'})
let f2:tf=()=>({name:'wang',skill:'singing'})
f2=f1//error!!! 这时候是f1的函数体功能是构建一个Person类型返回值,但它赋值给f2后,缺了skill,