类型挑战【转载】:https://wangtunan.github.io/blog/typescript/challenge.html#%E4%BB%8B%E7%BB%8D
工具类型
Partial<Type>
(可选)
-
解释:用来构造 (创建) 一个类型, 将
T
的所有属性设置为可选的interface User { id: number; name: string; } const obj1: Partial<User> = { id: 25 } // 正确
-
实现:
type Partial<T> = { [P in keyof T]?: T[P]; }
-
[P in keyof T]
通过映射类型,遍历T
上的所有属性 -
?:
语法称为映射修饰符,用于影响可选性。
-
-
常用场景一:构造一个新类型
interface Person { name: string; age: number; } type PartialPerson = Partial<Person>
-
常用场景二:获取由对象属性组成的类型
const obj = { id: 1, name: 'James', salary: 100, } type Test = Partial<typeof obj>;
必须使用
typeof
类型运算符,因为Partial
需要一个类型
映射修饰符可以以两种方式影响可选性,可以通过前缀 -
或者 +
(即-?:
/+?:
)删除或者添加这些修饰符,如果没有写前缀,相当于使用了 +
前缀
Required<Type>
(必选)
-
解释:声明类型中的每一项都是必需项
interface User { id?: number; name?: string; } const obj1: Required<User> = { id: 25 }; // Error 缺少属性 const obj2: Required<User> = { id: 25, name: '666' }; // 正确
-
实现:
type Required<T> = { [P in keyof T]-?: T[P]; }
- 这里的
-?
就是抵消掉问号?
- 这里的
Readonly<Type>
(只读)
-
解释:将
Type
的所有属性都设置为readonly
(只读 ), 构造出来的结构完全相同,但所有属性为只读interface User { des: string; } // type ReadonlyUser = Readonly<User> const todo: Readonly<User> = { des: "A student", }; todo.des = "Hello"; // Error 类型值不可再赋值改变
-
内部实现:
type Readonly<T> = { readonly [P in keyof T]: T[P]; }
- 主要实现是通过映射遍历所有
key
,然后给每个key
增加一个readonly
修饰符
- 主要实现是通过映射遍历所有
Record<Keys, Type>
-
构造一个对象类型,其属性键为
Keys
,属性值为Type
。此实用程序可用于将一个类型的属性映射到另一个类型。interface PageInfo { id: number; title: string; isCache: Boolean } type Page = "home" | "about" | "login"; type NavType = Record<Page, PageInfo> /** * => * type NavType ={ * home: PageInfo; * about: PageInfo; * login: PageInfo; * } */ const nav: NavType = { home: { id: 1, title: 'home Page', isCache: false }, about: { id: 1, title: 'home Page', isCache: false }, login: { id: 1, title: 'home Page', isCache: false }, }
-
实现:
type Record<K extends keyof any, T> = { [P in K]: T; }
核心实现就是遍历
K
,将值设置为T
;注意的是
keyof any
得到的是string | number | symbol
,原因在于类型key
的类型只能为string | number | symbol
Pick<Type, Keys>
(挑选)
-
从
Type
中选取一组属性Keys
(字符串字面值或字符串字面值的并集)来构造一个新类型。- 即:获取一个类型中的某些
key
interface User { name: string; age: number; address: string; } type PickUser = Pick<User, 'age' | 'name'>; /** * => * type PickUser = { * age: number; * name: string; * } */
- 即:获取一个类型中的某些
-
实现:
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }
-
extends
限制了K
的值必须属于Type
的属性值(keyof Type
)
-
Omit<Type, Keys>
(省略)
-
通过从
Type
中选取所有属性然后移除Keys
(字符串字面值或字符串字面值的并集)来构造类型。- 即:移除一个类型的某些
key
interface User { name: string; age: number; address: string; } type OmitUser = Omit<User, 'address'>; /** * => * type OmitUser = { * age: number; * name: string; * } */
- 即:移除一个类型的某些
-
1.利用
Pick
实现:type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
从联合类型
T
中排除K
后,剩下的属性构成一个新的类型,即‘我们需要的属性联合’利用
Pick
提取需要的Keys
组成的类型:Omit = Pick<T, 我们需要的属性联合>
-
2.利用映射类型实现:
type Omit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P]; }
- 类似
Pick
,通过映射类型遍历Exclude<keyof T, K>
的属性 -
T[P]
设置类型为原来的类型
- 类似
Exclude<T, U>
(排除)
从
T
中剔除可以赋值给U
的类型。(两者的差集)实现:
type Exclude<T, U> = T extends U ? never : T
遍历
T
中的所有子类型,如果该子类型约束于约束于U(存在于U、兼容于U),则返回never
,否则返回该子类型never
表示一个不存在的类型,与其它类型联合后是没有never
的
type T0 = Exclude<"a" | "b" | "c", "a">;
/**
* => type T0 = "b" | "c"
*/
Extract<T, U>
(提取)
-
提取
T
中可以赋值给U
的类型。(两者的交集)type Test1 = Extract<"a" | "b" | "c", "a" | "f">; // => type Test1 = "a"
-
实现:
type Extract<T, U> = T extends U ? T : never;
- 遍历
T
,T
的子类型存在于U
,则返回该子类型,否则返回never
- 遍历
NonNullable<Type>
(非空)
通过从
Type
中排除null
和undefined
来构造一个类型。-
实现:
type NonNullable<T> = T & {};
type T0 = NonNullable<string | number | undefined | null>; // => type T0 = string | number
Parameters<Type>
(参数的类型)
返回由函数参数类型组成的元组类型
-
实现:
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
首先约束参数
T
必须是一个函数类型,所以(...args: any) => any
替换成Function
也是可以的;具体实现:判断
T
是否是个函数类型,如果是则使用infer P
让TS
自己推导出函数的参数类型,并将推导结果存到类型P
上,否则就返回never
示例:
type T0 = Parameters<() => string>; // => type T0 = [] type T1 = Parameters<(s: string) => void>; // => type T1 = [s: string] type T2 = Parameters<<T>(arg: T) => T>; // => type T2 = [arg: unknown] declare function f1(arg: { a: number; b: string }): void; type T3 = Parameters<typeof f1>; // => // type T3 = [arg: { // a: number; // b: string; // }] type Eg = Parameters<(arg1: string, arg2: number) => void>; // => type Eg = [arg1: string, arg2: number] // 这是一个元组 function sum(a: number, b: number): number { return a + b; } type SumParamsType = Parameters<typeof sum>; // => type SumParamsType = [a: number, b: number]
扩展infer
关键词
infer
的作用是让TS
自己推导类型,并将推导结果存储在其参数绑定的类型上。infer
只能在extends
条件类型上使用,不能在其它地方使用。-
扩展:
infer
实现一个推导数组所有元素的类型:/** * 约束参数T为数组类型, * 判断T是否为数组,如果是数组类型则推导数组元素的类型 */ type FalttenArray<T extends Array<any>> = T extends Array<infer P> ? P : never; type Eg1 = FalttenArray<[number, string]> // => type Eg1 = number | string; type Eg2 = FalttenArray<[1, 'asd']> // => type Eg2 = 1 | "asd"
ReturnType<Type>
(返回值类型)
获取函数的返回值类型
-
实现:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
- 和
Parameters<Type>
相比,ReturnType<Type>
只是将infer R
从参数位置移到返回值位置,此时R
表示待推断的返回值类型
- 和
-
示例:
type Func = () => { a: number; b: string }; type Test = ReturnType<Func>; // => // type Test = { // a: number; // b: string; // } type T0 = ReturnType<() => string>; // type T0 = string type T1 = ReturnType<(s: string) => void>; // type T1 = void type T2 = ReturnType<<T>() => T>; // type T2 = unknown type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // type T3 = number[]
非法的例子:均不满足
(...args: any): any
,type T
将被视为any
处理。type T = ReturnType<string>; // Error type T = ReturnType<Function>; // Error
ConstructorParameters<Type>
(获取构造函数参数类型)
可以获取类的构造函数的参数类型,存在一个元组中。(如果
type
不是函数,则为never
类型)。-
实现:
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never
核心还是用
infer
推导构造函数的参数类型首先约束参数
T
是拥有构造函数的类,判断T
是满足约束的类时,利用infer P
自动推导构造函数的参数类型,并最终返回该类型。new (...args: any)
是构造签名,new (...args: any) => any
是构造函数类型字面量
-
示例:
type Test = ConstructorParameters<(new (name: string) => any) | (new (age: number) => any)>; // => type Test = [name: string] | [age: number] class Person { name: string; age: number; constructor( name: string, age: number) { this.name = name; this.age = age; } } type PersonParamsType = ConstructorParameters<typeof Person> // => type PersonParamsType = [name: string, age: number]
扩展1:为什么要对T
约束为抽象类(abstract
)
abstract
用来定义抽象类以及抽象类中的抽象方法
// 普通类
class Test {}
// 抽象类
abstract class TestAbst {}
const T1: typeof Test = Test // 可以赋值
const T2: typeof Test = TestAbst // Error: 无法将 抽象构造函数类型 分配给 非抽象构造函数类型。
const TAbs1: typeof TestAbst = Test // 可以赋值
const TAbs2: typeof TestAbst = TestAbst // 可以赋值
- 可以将抽象类(抽象构造函数)赋值给抽象类或者普通类,反之不行。
扩展2:关于类类型的表示
如上const T1: typeof Test = Test
,虽然用typeof
表示类的类型非常方便(其实它不是用来干这个的, 至少不全是),但不能因每次要写一个类类型时都先用class
关键词定义一个类。
替代方法:使用构造函数表示class的类型
type PClass = new (name: string, age: number) => { name: string, age: number };
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const TestPerson: PClass = Person
也可以在接口interface
中使用:
interface PInter {
new (name: string, age: number): { name: string, age: number }
}
const TestPerson2: PInter = Person
InstanceType<Type>
(获取构造函数返回值的类型)
InstanceType
用于获取类的实例化类型,即 new 类
的产物,可以说是执行构造函数的返回值,与上面的ConstructorParameters
类型相对应
-
实现:
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
- 与
ConstructorParameters<Type>
相比,只需要对infer
的使用换了个位置。
class Person { constructor(name?: string, age?: number) { } } const T: InstanceType<typeof Person> = new Person() type T1 = InstanceType<typeof Person> type T2 = InstanceType<any> type T3 = InstanceType<never> type T4 = InstanceType<string> // Error: string 不满足约束 abstract new (...args: any) => any type T5 = InstanceType<Function> // Error: Function 不满足约束 abstract new (...args: any) => any
- 与
ThisParameterType<Type>
(函数参数this
的类型)
-
获取函数参数的
this
的类型,常用在call,apply,bind
中。如果函数内部的第一个参数命名不是this
,则会返回unknown
function getThis(this: string, a: number) { console.log(a, this); } type getThisType = ThisParameterType<typeof getThis>; // string /****2******/ function getThis(a: number) { console.log(a); } type getThisType = ThisParameterType<typeof getThis>; // unknown /****3******/ function fncTest(this: Number) { return this.toString(16); } function fncTestThis(n: ThisParameterType<typeof fncTest>) { // (parameter) n: Number return fncTest.apply(n); }
实现:
type ThisParameterType<T> = T extends (this: infer U, ...args: never) => any ? U : unknown;
OmitThisParameter<Type>
-
获取不带
this
参数的新函数类型。同ThisParameterType<Type>
,this
必须是函数的第一个参数。function getThis(this: string, a: number) { console.log(a, this); } type getThisType = OmitThisParameter<typeof getThis>; // type getThisType = (a: number) => void
实现:
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;
ThisType<Type>
(上下文this
类型的标记)
此实用程序不返回转换后的类型。相反,它充当上下文类型的标记。(Tips:必须启用noImplicitThis
标志才能使用此实用程序)
-
官网示例与解释:
// Compile with --noImplicitThis type ObjectDescriptor<D, M> = { data?: D; methods?: M & ThisType<D & M>; // 方法中的“this”类型是 D & M }; function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M { let data: object = desc.data || {}; let methods: object = desc.methods || {}; return { ...data, ...methods } as D & M; } let obj = makeObject({ data: { x: 0, y: 0 }, methods: { moveBy(dx: number, dy: number) { this.x += dx; // 强类型this this.y += dy; // 强类型this }, }, }); obj.x = 10; obj.y = 20; obj.moveBy(5, 5);
上示例中,
makeObject
参数中的对象属性methods
有一个上下文类型:ThisType<D & M>
,因此对象中methods
属性下的方法中的this
类型是{x: number, y: number} & {moveBy(dx: number, dy: number): void}
。ThisType<T>
的接口,在lib.d.ts
只是被声明为空的接口,除了可以在对象字面量上下文中可以被识别以外,该接口的作用等同于任意空接口。
-
(简单理解)
-
ThisType
主要用于显式指定对象方法中this
的类型,在JS
中this
引用的是当前方法所在的对象,但在TS
中,有时需要更准确的指定this
的类型;例如当对象有多个方法时,需要在每个方法中指定
this
的类型,以确保这些方法在使用this
时不会发生类型错误;当对象方法作为参数传递给其它函数时,需要在类型中指定
this
类型,以确保在使用函数时this
引用正确;
interface Person { name: string; age: number; sayHi(): string; } // 使用 ThisType 来扩展 Person 接口并显式指定 this 的类型 type PersonWithGreeting = Person & ThisType<Person>; // greet中 使用 this: PersonWithGreeting 参数来指定 this 的类型 function greet(this: PersonWithGreeting, greeting: string) { return `${greeting}, ${this.name}!`; } const person: PersonWithGreeting = { name: "Tom", age: 20, sayHi() { return `Hi, ${this.name}!`; }, }; // 最后,通过 call() 方法调用 greet 方法,并指定 person 作为 this 对象, // 这样,在 greet 方法中,this 就表示 person 对象 const greeting = greet.call(person, "Hello"); console.log(greeting); // Hello, Tom! console.log(person.sayHi()); // Hi, Tom!
-
内部字符串操作类型
Uppercase<StringType>
将StringType
转为大写Lowercase<StringType>
将StringType
转为小写Capitalize<StringType>
将StringType
首字母大写Uncapitalize<StringType>
将StringType
首字母小写
type Eg1 = Uppercase<'abcd'>; // type Eg1 = "ABCD"
type Eg2 = Lowercase<'ABCD'>; // type Eg2 = "abcd"
type Eg3 = Capitalize<'Abcd'>; // type Eg3 = "Abcd"
type Eg4 = Uncapitalize<'aBCD'>; // type Eg4 = "aBCD"
扩展:自定义工具类型
1.获取不同时存在于 T
和 U
内的类型
实现:type Exclusive<T, U> = Exclude<T | U, T & U>;
// 实现
type Exclusive<T, U> = Exclude<T | U, T & U>;
// 示例
type Eg = Exclusive<"a" | "b" | "c", "Er" | "a" | "b"> // type Eg = "c" | "Er"
- 主要是利用
Exclude
获取存在与第一个参数但不存在与第二个参数的类型 - 参数二
T & U
获取的是所有类型的交叉类型; - 参数一
T | U
这是利用在联合类型在extends
中的分发特性,可以理解为Exclude<T, T & U> | Exclude<U, T & U>
;
2.获取T
中所有类型为函数的key
组成的联合类型
type FunctionKeys<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
interface Test {
foo(): void;
bar: string;
baz: () => number;
}
type TestFunction = FunctionKeys<Test>; // "foo" | "baz"
-
实现:
K in keyof T
: 先使用keyof T
获取T
中所有key
组成的联合类型, 然后使用 映射类型 将这个联合类型转换为一个新的对象T[]
是索引访问操作,可以取到值的类型;若
T[K]
为有效类型,则判断是否为Function
类型,是的话返回K
,否则never
;-
最后执行
{...}[keyof T]
索引访问获取最终结果Tips1:
T[keyof T]
则是获取T
所有值的类型;Tips2:
never
和其他类型进行联合时,never
是不存在的,例如:never | number | string
等同于number | string
3.获取对象中指定类型的字段(由2改)
根据指定类型U
,在对象T
中挑选出所有类型一致的字段并组成一个新的类型。
type TypeKeys<T, U> = {
[K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
type PickByType<T, U> = Pick<T, TypeKeys<T, U>>;
interface Example {
foo: string;
bar: number;
baz: string;
}
type StringFieldsOfExample = PickByType<Example, string>; // { foo: string, baz: string }
type NumberFieldsOfExample = PickByType<Example, number>; // { bar: number; }
4.从数组中提取指定属性的值(由2/3改)
type PickKeys<T, K extends keyof T> = {
[P in K]: T[P]
}[K];
function pluck<T, K extends keyof T>(arr: T[], key: K): PickKeys<T, K>[] {
return arr.map((item) => item[key]);
}
interface Example3 {
foo: string;
bar: number;
}
const exampleList: Example3[] = [
{ foo: 'hello', bar: 1 },
{ foo: 'world', bar: 2 },
];
const fooList = pluck(exampleList, 'foo');
console.log('fooList', fooList); // ["hello", "world"]
通过PickKeys
从对象中提取指定属性的值。定义pluck
函数,用于从数组中提取指定属性的值,并返回一个新的数组
5.查找T
所有非只读类型的key
组成的联合类型
/**
* 核心实现
*/
type MutableKeys<T extends object> = {
[P in keyof T]-?: IfEquals<
{ [Q in P]: T[P] },
{ -readonly [Q in P]: T[P] },
P
>;
}[keyof T];
/**
* @desc 一个辅助类型,判断X和Y是否类型相同,
* @returns 是则返回A,否则返回B
*/
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
? A
: B;
示例:MutableKeys
只适用一维对象,无法对嵌套对象生效
/**
* 示例
*/
interface Person {
readonly name: string;
age: number;
address: {
readonly street: string;
city: string;
};
}
type T0 = MutableKeys<Person> // type T0 = "age" | "address"
// 测试
type Test = Pick<Person, MutableKeys<Person>>
/**
* =>
* type Test = {
* age: number;
* address: {
* readonly street: string;
* city: string;
* };
* }
*/
const person: Person = {
name: "Alice",
age: 30,
address: {
street: "123 Main St",
city: "Springfield",
},
};
const P2: Test = {
age: 32,
address: {
street: "123 Main St",
city: "Springfield",
},
};
6.获取T
中所有的可选项或key
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];
使用映射遍历所有Key
,通过Pick<T, K>
提取当前Key
和类型;
利用{} extends {当前key: 类型}
判断是否为可选类型。
// Eg2 = false
type Eg2 = {} extends {key1: string} ? true : false;
// Eg3 = true
type Eg3 = {} extends {key1?: string} ? true : false;
利用的就是{}
和只包含可选参数类型{key?: string}
是兼容的这一特性。把extends
前面的{}
替换成object
也是可以的。
/**
* 测试例
*/
interface E1 {
a: string;
b?: number;
c?: boolean;
c2: string;
}
/**
* T 中所有可选项的key的联合类型
* type K1 = "b" | "c"
*/
type K1 = OptionalKeys<E1>;
/**
* T 中所有可选项
* type T1 = {
* b?: number | undefined;
* c?: boolean | undefined;
* }
*/
type T1 = Pick<E1, K1>
7.Pick
扩展:获取T
中指定类型的所有项
/**
* 辅助函数
* 用于获取 T 中类型不为 never 的类型组成的联合类型
*/
type TypeKeys<T> = T[keyof T];
/**
* 核心实现
* undefined 的存在 可以获取可选项的值
*/
type PickByValue<T, V> = Pick<T,
TypeKeys<{ [P in keyof T]: T[P] extends V | undefined ? P : never }>
>;
// 示例
interface E1 {
a: string;
a2?: string;
b: number;
b1: number | string;
b2?: number;
}
type T1 = PickByValue<E1, string>
/**
* type T1 = {
* a: string;
* a2?: string | undefined;
* }
* ----------------------------------------
* 注意: 如果将 T[P] extends V | undefined 中的 undefined 去掉,则拿到的值为
* type T1 = {
* a: string;
* }
*/
如果 T
中属性的值可能是多个类型并且这些类型之间存在兼容性关系(比如 number | string
),需要使用 Extract
类型来进行类型匹配。
type TypeKeys<T> = T[keyof T];
type PickByValueExact<T, V> = Pick<T,
TypeKeys<{[P in keyof T]: Extract<T[P], V> extends never ? never : P}>
>;
interface E1 {
a: string;
a1: string | boolean;
b: number;
b1: number | string;
c?: boolean;
}
type T1 = PickByValueExact<E1, boolean>
/**
* type T1 = {
* a1: string | boolean;
* c?: boolean | undefined;
* }
*/
8.删除T
中指定类型的所有项(由7改)
type TypeKeys<T> = T[keyof T];
type OmitByValueExact<T, V> = Omit<T,
TypeKeys<{[P in keyof T]: Extract<T[P], V> extends never ? never : P}>
>;
/**
* 等同于(Pick写法) =>
* type OmitByValueExact<T, V> = Pick<T,
* TypeKeys<{ [P in keyof T]: Extract<T[P], V> extends never ? P : never }>
* >;
*/
// 示例
interface E1 {
a: string;
a1: string | boolean;
b: number;
b1: number | string;
c?: boolean;
}
type T1 = OmitByValueExact<E1, boolean>
/**
* type T1 = {
* a: string;
* b: number;
* b1: number | string;
* }
*/
9.Pick
扩展:获取 T
和U
共同存在key
和对应类型
// T extends object, U extends object
type Intersection<T, U> = Pick<T, Extract<keyof U, keyof T> & Extract<keyof T, keyof U>>
// 使用
interface E1 {
a: string;
a1: string | boolean;
b: number;
b2?: number;
}
interface E2 {
a: string;
a1: boolean;
b2?: number;
}
type T1 = Intersection<E1, E2>
// type T1 = {
// a: string;
// a1: string | boolean;
// b2?: number | undefined;
// }
2次Extract
的原因是为了避免类型的兼容推导问题。
10.去除T
中存在于U
的key
和对应类型(由9改)
type Diff<T, U> = Pick<T, Exclude<keyof T, keyof U>>
// 使用
interface E1 {
a: string;
a1: string | boolean;
b: number;
b2?: number;
}
interface E2 {
a: string;
a1: boolean;
b2?: number;
c: string
}
type T1 = Diff<E1, E2>
// type T1 = {
// b: number;
// }
11.Overwrite
和 Assign
(9/10改)
Assign
// 合并,相同key和对应类型由后者覆盖前者
type Assign<T, U, I = Diff<T, U> & U> = Pick<I, keyof I>;
// 示例
interface E1 {
a: string;
b: string | boolean;
}
interface E2 {
a: string;
c: string
}
type Eg2 = Assign<E1, E2>
// type Eg2 = {
// a: string;
// b: string | boolean;
// c: string;
// }
Overwrite
// 获取前者独有的key和类型,再取两者共有的key和该key在后者中的类型,最后合并。
// T extends object, U extends object
type Overwrite<T, U, I = Diff<T, U> & Intersection<U, T>> = Pick<I, keyof I>
interface E1 {
a: string;
b3: number;
}
interface E2 {
a: string;
a1: boolean;
b2: number;
c: string
}
type Eg1 = Overwrite<E1, E2>
// type Eg1 = {
// a: string;
// b3: number;
// }
12.将联合类型转变成交叉类型
type UnionToIntersection<T> = (
T extends any ? (arg: T) => void : never
) extends (arg: infer U) => void ? U : never;
interface A {
a: string;
}
interface B {
b: number;
}
type C = UnionToIntersection<A | B>;
// { a: string } & { b: number }
T extends any ? (arg: T) => void : never
: 如果类型T
可以被转化为任意类型,则返回一个接受参数为T
类型的函数;否则返回never
类型。(arg: infer U) => void
:infer
关键字来推断函数类型所接收的参数类型并定义一个新的类型U
利用第二个
extends
配合infer
推导得到U
的类型,利用infer
对协变类型的特性得到交叉类型。