ts相关笔记(extends、infer、Pick、Omit)
最近刷了本ts小册,对一些知识点做下笔记。
extends
extends 是一个关键字,用于对类型参数做一些约束。
A extends B 意味着 A 是 B 的子类型,比如下面是成立的
- ‘abc’ extends string
- 599 extends number
看下面例子:
type MyType<T extends number> = {
value: T;
};
const example: MyType<number> = {
value: 42
};
联合类型子集均为联合类型的子类型,即 1、 1 | 2 是 1 | 2 | 3 | 4 的子类型
type MyType<T extends number | string> = {
value: T;
};
const example1: MyType<number> = {
value: 42
};
const example2: MyType<string> = {
value: 'hello'
};
通过接口进行约束
interface MyInterface {
length: number;
}
type MyType<T extends MyInterface> = {
data: T;
};
const example: MyType<string> = {
data: 'hello' // Error: 'string' does not satisfy the constraint 'MyInterface'
};
const validExample: MyType<{ length: number }> = {
data: { length: 5 }
};
根据传入的请求码判断请求是否成功
type ResStatus<ResCode extends number> = ResCode extends 10000 | 10001 | 10002
? 'success'
: 'failure';
type Res1 = ResStatus<10000>; // "success"
type Res2 = ResStatus<20000>; // "failure"
type Res3 = ResStatus<'10000'>; // 类型“string”不满足约束“number”。
如果我们想让这个类型别名可以无需显式传入泛型参数也能调用,并且默认情况下是成功地,这样就可以为这个泛型参数声明一个默认值:
type ResStatus<ResCode extends number = 10000> = ResCode extends 10000 | 10001 | 10002
? 'success'
: 'failure';
type Res4 = ResStatus; // "success"
infer
通过 infer 关键字来在 条件类型 中提取类型的 某一部分信息
让 TypeScript 根据上下文自动推断出我们需要的类型,从而简化代码并让类型信息更具灵活性
看下面例子来理解
假设我们有一个条件类型 ExtractReturnType,它接受一个类型参数 T。我们希望当 T 是一个函数类型时,从这个函数类型中提取出它的返回值类型;当 T 不是函数类型时,返回never类型。
type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function greet(): string {
return 'Hello!';
}
现在,我们可以使用 ExtractReturnType 来推断 greet 函数的返回值类型,而不需要显式地声明它:
const result: ExtractReturnType<typeof greet> = 'Hello World';
ExtractReturnType 就会被推断为 string 类型 👇
TypeScript 内置的声明文件es5.d.ts中使用infer举例
-
获取函数类型中this的参数类型
type ThisParameterType<T> = T extends (this: infer U, ...args: never) => any ? U : unknown;
class Person { name: string; constructor(name: string) { this.name = name; } getName(this: Person): string { return this.name; } } type testThisType = ThisParameterType<typeof Person.prototype.getName>; // Person
-
从函数类型中移除 this 参数
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;
function sayHello(this: { name: string }) { console.log(`Hello, ${this.name}!`); } type WithoutThisParam = OmitThisParameter<typeof sayHello>; // () => void
-
获取函数类型中参数的类型列表
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
function sum(a: number, b: number) { return a + b; } type SumParams = Parameters<typeof sum>; // [a: number, b: number]
Pick 和 Omit
都是对结构处理的工具类型,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>>;
Pick,它接受两个泛型参数,第一个参数 T 即是我们会进行 结构处理 的 原类型(一般是对象类型),第二个参数 K 则是被约束为 T 类型的 键名联合类型 (就是第一个参数传一个对象,第二个参数传 对象的键 (key),最后获取到一个新对象类型)
Omit ,与Pick相反,Pick 是保留这些传入的键, Omit 则是移除这些传入的键取剩下的
使用举例
interface Foo {
name: string;
age: number;
job: string;
}
type PickedFoo = Pick<Foo, "name" | "age">
// 等同于
// type PickedFoo = {
// name: string;
// age: number;
// }
interface Foo {
name: string;
age: number;
job: string;
}
type OmitedFoo = Omit<Foo, 'name'>
// 等同于
// type OmitedFoo = {
// age: number;
// job: string;
// }