infer的基础作用
一个函数定义,A->B,我们可能需要细化成出,函数的参数类型 A或者返回值类型B,这个关键字就是完成这个用途。
interface User {
name: string;
age: number;
}
type Func = (user: User) => void;
type ParamType<T> = T extends (param: infer P) => any ? P : T;
type Test = ParamType<Func>; // Test = User
type s = ParamType<string>; // string
在这个条件语句 T extends (param: infer P) => any ? P : T 中,infer P 表示待推断的函数参数。
整句表示为:如果 T 能赋值给 (param: infer P) => any,则结果是 (param: infer P) => any 类型中的参数 P,否则返回为 T。
它也可以表示返回值
type ReturnType<T> = T extends (...args: any[]) => infer P ? P : any;
type Func2 = () => User;
type Param = ParamType<Func>; // Param = User
简单情况下,P就是输入函数参数或者返回值对应的类型。
infer与逆变和逆变
详细分析关键字之前,先回顾一下协变和逆变。
约定A → B 指的是以 A 为参数类型,以 B 为返回值类型的函数类型。
先有如下定义
class A { }
class B extends A { }
class C extends B { }
function f(g: (arg:B) => B){
}
B继承于A,C继承于B, 有个函数f,要求输入一个参数g的类型是 B -> B
当我们输入g:arg->back时,arg和back需要满足以下条件才能通过检验 ,
arg是B及其超类(此处是A)back是B及其子类(此处是C)
这种情况我们称,函数的返回值类型是协变的,而参数类型是逆变的。
提出一个问题,以上面的示例来说,f要求g的类型时, g extends B -> B 还是 B -> B extend g ?
参数需要满足要求,显然是前者。设T:a -> b, T extends P -> K 为真,
那么,P到a是逆变的,K到b是协变的
我们看一个经典且常用的例子,联合类型转交叉类型。
我们首先需要知道extends的distributive(分配律)特性,简单来说,就是
type F<T> = T extends U ? X : Y
type union_type = A | B | C;
type a = F<union_type>;
//那么a的结果为 A extends U ? X :Y | B extends U ? X :Y | C extends U ? X : Y
更加详细的特性,可以看这篇文章:深入typescript类型系统(二): 泛型和类型元编程
然后我们写出这个函数
type unionDistribute<U> = U extends any ? (_: U) => void : never
type a = unionDistribute<T1 | T2>
// (_:T1 => void) | (_:T2) => void
我们再利用在逆变位置上(此处为参数位置), 同一类型变量的多个候选类型将会被推断为交叉类型的特性
type Bar<T> = T extends (_:infer U) => void ? U : never;
type T21 = Bar<a: (x: T1) => void | b: (x: T2) => void>; // T1 & T2
最后将这两个泛型函数复合就可以得到我们的结果
type UnionToIntersection<U> = Bar<unionDistribute<U>>
type a1 = UnionToIntersection<{ a: number } | { b: string }>
//{a: number } & {b: string }
我们应该如何理解从
{ a: number } & { b: string }到 { a: number } | { b: string } 是逆变的呢?
interface A { a: number }
interface B { b: string }
interface C extends A, B { }
以上面的示例来说,A与B被C继承,那么C就拥有A和B的所有属性,我们知道 { a: number } & { b: string }等同于 { a: number , b: string },所以C到A | B是逆变的。
再举一个简单的例子吧,UnionToIntersection<"a" | "b">的结果是"a"&"b"等价于never。never类型是任何类型的子类型 ,也就是说never到"a" | "b"是逆变的。
内置类型
typescript中也内置了很多常用类型
-
用于提取构造函数中参数(实例)类型:
一个构造函数可以使用
new来实例化,因此它的类型通常表示如下:type Constructor = new (...args: any[]) => any;当
infer用于构造函数类型中,可用于参数位置new (...args: infer P) => any;和返回值位置new (...args: any[]) => infer P;。因此就内置如下两个映射类型:
// 获取构造函数参数类型 type ConstructorParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer P) => any ? P : never; // 获取返回实例类型 type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any; class TestClass { constructor(public name: string, public age: number) {} } type Params = ConstructorParameters<typeof TestClass>; // [string, number] type Instance = InstanceType<typeof TestClass>; // TestClass
typescript真的太难了/(ㄒoㄒ)/~~ 不研究它了哼