TypeScript的内置类型

使用TS开发项目有好久了,经常使用到TS一些内置的的工具类型,非常实用。对于一些不是经常使用的类型,每次得看文档,今天就记录一下,加深记忆
参考地址: https://www.typescriptlang.org/docs/handbook/utility-types.html

类型后面的Vx.x 表示是那个版本发布此类型

Awaited V4.5

这个工具类型比较有用, 可以返回 异步函数(async)/ promise的.then 方法的结果值类型,尤其是可以递归解包。

1
2
3
4
5
6
7
8
9
10
11
type A = Awaited<Promise<string>>;

type A = string

type B = Awaited<Promise<Promise<number>>>;

type B = number

type C = Awaited<boolean | Promise<number>>;

type C = number | boolean

如果不考虑递归,可以利用 infer 实现一个简单的 Awaited

1
2
3
4
type MyAwaited<T> = T extends Promise<infer R> ? R : T;

type A = MyAwaited<Promise<string>>;
// type A = string

Partial V2.1

将所有属性变为 可选 的,这个比较实用,尤其在复用一些 type/interface 的时候,

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Person {
name: string
age: number
address: string
}

type Person2 = Partial<Person>
// 相当于
interface Person2 {
name?: string
age?: number
address?: string
}

Required V2.8

这个和 Partial 相反,是把所有属性变为必选

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Person {
name?: string
age?: number
address?: string
}

type Person2 = Required<Person>
// 相当于
interface Person2 {
name: string
age: number
address: string
}

Readonly V2.1

将某个类型里的属性全部变为只读, 这个貌似日常开发不怎用,可以参考 Object.freeze 方法,

1
2
// Object.freeze
freeze<T>(o: T): Readonly<T>;

Record<Keys, Type> V2.1

这个也比较实用,能构造一个具有指定类型(Type)的一组属性(Keys)的类型,尤其是将一个类型的属性映射到另一个类型的属性是,很方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface EmployeeType {
id: number
fullname: string
role: string
}

let employees: Record<number, EmployeeType> = {
0: { id: 1, fullname: "John Doe", role: "Designer" },
1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
2: { id: 3, fullname: "Sara Duckson", role: "Developer" },
}

// 或者创建一个类型
type A = Record<string,number>
// 相当于
type A = {
[k: string]: number;
}

Pick<Type, Keys> V2.1

选择某个类型中 指定的一组属性(keys),返回一个相当于这个类型(type)的子类型,平时用的也比较多吧

1
2
3
4
5
6
7
8
9
10
11
12
interface Todo {
title: string;
description: string;
completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
title: "Clean room",
completed: false,
};

Omit<Type, Keys> V3.5

从某个类型中,移除(忽略)某些属性,返回剩余的属性组成新的类型,行为和Pick 刚好相反。也经常使用

1
2
3
4
5
6
7
8
9
10
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
// 查看源码,就是 Pick ,Exclude 配合使用

interface Person {
name: string
age: number
addr: string
}
type PurPersion1 = Omit<Person,"addr"> // {name, age}
type PurPersion2 = Omit<Person,"addr"|"age"> // {name}

Exclude<UnionType, ExcludedMembers> V2.8

排除某个集合中的类型,

1
2
3
4
5
6
7
type UnionList = string | number | (() => void) | Array<any>

// 排除了函数类型
type ExFuncList = Exclude<UnionList, Function> // string | number | Array<any>

// 排除了 "a"
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"

Extract<Type, Union> V2.8

Type 中 提取 满足(存在)Union中的类型取出,去掉不存在的,就像是取 两个类型的交集 平时业务开发很少用到,一些工具类库,框架用的比较多

1
2
3
4
5
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
// type T0 = "a"

type T1 = Extract<string | number | (() => void), Function>;
// type T1 = () => void

NonNullable V2.8

排除 null undefined 属性

1
2
3
4
5
6
type T0 = NonNullable<string | number | undefined>;

type T0 = string | number
type T1 = NonNullable<string[] | null | undefined>;

type T1 = string[]

Parameters V3.1

这个类型可以用来提取函数中的参数,返回一个元组,这里贴出了官方的例子,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
declare function f1(arg: { a: number; b: string }): void;

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]
type T3 = Parameters<typeof f1>;

type T3 = [arg: {
a: number;
b: string;
}]

type T4 = Parameters<any>;

type T4 = unknown[]
type T5 = Parameters<never>;

type T5 = never
type T6 = Parameters<string>;
// Error Type 'string' does not satisfy the constraint '(...args: any) => any'.

type T6 = never
type T7 = Parameters<Function>;
// Error Type 'Function' does not satisfy the constraint '(...args: any) => any'.
// Error Type 'Function' provides no match for the signature '(...args: any): any'.

type T7 = never

ConstructorParameters

用来提取构造函数的参数,返回一个元组类型, 查看源码可以看到,有两个点需要注意:

  • abstract关键字修饰的函数叫抽象方法,而抽象方法只能出现在抽象类中
  • new (…args: any) => any,这就是对构造函数的定义

所以,这个工具类型只是针对抽象类发挥效果,never和any我们不需要关心。 平时基本很少用

1
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;

ReturnType V2.8

获取函数返回参数类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
declare function f1(): { 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[]

type T4 = ReturnType<typeof f1>;
type T4 = {
a: number;
b: string;
}
type T5 = ReturnType<any>;

type T5 = any

type T6 = ReturnType<never>;
type T6 = never

type T7 = ReturnType<string>;
// Error Type 'string' does not satisfy the constraint '(...args: any) => any'. l
type T7 = any


type T8 = ReturnType<Function>;
// Error Type 'Function' does not satisfy the constraint '(...args: any) => any'.
// Error Type 'Function' provides no match for the signature '(...args: any): any'.
type T8 = any

InstanceType V2.8

返回基于构造函数返回值的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class C {
x = 0;
y = 0;
}

type T0 = InstanceType<typeof C>;
type T0 = C

type T1 = InstanceType<any>;
type T1 = any

type T2 = InstanceType<never>;
type T2 = never

type T3 = InstanceType<string>;
// Error Type 'string' does not satisfy the constraint 'abstract new (...args: any) => any'.
type T3 = any

type T4 = InstanceType<Function>;
// Error Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'.
// Error Type 'Function' provides no match for the signature 'new (...args: any): any'.
type T4 = any

ThisParameterType V3.3

获取函数 this 参数的类型, 如果没有则返回 unkown, 这个貌似在平时业务开发很少使用。

官方demo:

1
2
3
4
5
6
7
function toHex(this: Number) {
return this.toString(16);
}

function numberToString(n: ThisParameterType<typeof toHex>) {
return toHex.apply(n);
}

前置知识点:this

  • this参数只能叫 this,且必须在参数列表的第一个位置
  • this 必须是显式定义的
  • 这个 this 参数在函数实际被调用的时候不存在,不需要显式作为参数传入,而是通过 call、apply或者是 bind 等方法指定

OmitThisParameter V3.3

如果定义了 this 参数类型,就返回一个仅是去掉了 this 参数类型的新函数类型。对于没有,直接返回这个函数类型,

1
2
3
4
5
6
7
function toHex(this: Number) {
return this.toString(16);
}

const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);

console.log(fiveToHex());

ThisType V2.3

这个类型比较特殊,查看源码可以发现,定义了一个空接口. interface ThisType<T> { }

按照文档说法: 这个工具并不返回一个转换后的类型。相反,它作为一个上下文的this类型的标记。注意,必须启用 noImplicitThis 标志才能使用这个工具类型

直接从翻译结果来看,比较晦涩难懂,结合文档的demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>; // Type of 'this' in methods is 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; // Strongly typed this
this.y += dy; // Strongly typed this
},
},
});

obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);

把 上面的demo 直接复制到 typescript playgroud 中去,把鼠标移到this,可以看看提示的类型,你会发现 this 的类型为

1
2
3
4
5
6
{
x: number;
y: number;
} & {
moveBy(dx: number, dy: number): void;
}

先从简单的一点开始, 先声明一个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let obj = {
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx; // Property 'x' does not exist on type '{ moveBy(dx: number, dy: number): void; }'
this.y += dy; // Property 'y' does not exist on type '{ moveBy(dx: number, dy: number): void; }'
},
},
};

// 可以看到 this 默认推导的类型是 { moveBy(dx: number, dy: number): void; } , 也就是它的上一层对象 , 并没有x,y 属性

// 注意区别 :如果是箭头函数 就是 gloablThis (浏览器环境默认的上下文就是 window)

// 这个问题也可以通过显式的指明 this,
let obj = {
data: { x: 0, y: 0 },
methods: {
moveBy(this: { x: 0; y: 0 }, dx: number, dy: number) {
this.x += dx;
this.y += dy;
},
},
};

正常情况下,这种简单场景显式指定 this 就能满足使用

回头看看官方demo 的结构,非常类似 vue2.0 API 的, data ,methods ,…等结构,我们可以定义 多个data/methods ,之前的方法 就无法满足 这种可变未知的情况。
ts 是通过静态代码分析推断出的类型,但是在实际运行阶段,this是可能变化的,
所以仅仅靠依赖代码分析是无预测 this 类型的,

假设只考虑 data 、methods 两个属性, 于是就可以利用 泛型 ObjectDescriptor 和 一个工具方法 makeObject

1
2
3
4
5
6
7
8
9
10
11
12
type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M ,重点是这里,ThisType 来标记(重)一个 this 的类型
};

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
let data: object = desc.data || {};
let methods: object = desc.methods || {}; // methods 已经变成了 M & ThisType<D & M>
return { ...data, ...methods } as D & M; // 使用断言 指定一个类型
}
// 这样 desc 类型 已经是类型安全的了。 然后在 methods 中 this 就变为了 <data & methods>,

Intrinsic String Manipulation Types 字符串操作类型

以下几个类型都是操作字符串
Uppercase // 大写
Lowercase // 小写
Capitalize // 首字母大写
Uncapitalize // 首字母小写

作者

Fat Dong

发布于

2023-02-01

更新于

2023-02-01

许可协议