2023-06-13
TypeScript
00
请注意,本文编写于 915 天前,最后修改于 650 天前,其中某些信息可能已经过时。

目录

运算符
空值合并运算符 ??
非空断言运算符 !
可选链运算符 ?.
操作符
键值获取 keyof
实例类型获取 typeof
遍历属性 in
内置高级类型
Parameters
ReturnType
ConstructorParameters
InstanceType
ThisParameterType
Partial
Required
Readonly
Pick
Record<K, T>
Exclude<T, U>
Extract
Omit<T, K>
Awaited
Uppercase、Lowercase、Capitalize、Uncapitalize
模式匹配
字符串
StartsWith

那些你忽略的typescript小技巧

运算符

空值合并运算符 ??

?? 与 || 功能很相似,不同的是 ??在左侧表达式为 nullundefined 时,才会右侧表达式。

const num = a ?? 10; // 上面的写法等价于下面的写法 const num2 = ( a !== null && a !== void 0) ? a : 10;

在这里科普一下,void 0 会返回 undefined,那这里为什么不直接写undefined呢,是因为undefined是全局作用域中的一个变量,undefined的最初值就是原始数据类型undefined。 ES5之后的标准中,规定了全局变量下的undefined值为只读,不可改写的,但是局部变量中依然可以对之进行改写。 所以使用void 0来代替undefined就比较稳妥,防止出错。

非空断言运算符 !

这个运算符可以用在变量名或者函数名之后,用来强调对应的元素是非 null|undefined 的

js
function clickHandle(){ this.domRef!.open() // ! 告诉编译器 变量不为null 或 undefinded }

可选链运算符 ?.

相比上面!作用于编译阶段的非空判断,?.这个是开发者最需要的运行时(当然编译时也有效)的非空判断。

obj?.prop obj?.[index] func?.(args)

?.用来判断左侧的表达式是否是 null | undefined,如果是则会停止表达式运行,可以减少我们大量的&&运算。 比如我们写出a?.b时,编译器会自动生成如下代码

a === null || a === void 0 ? void 0 : a.b;

操作符

键值获取 keyof

keyof 可以获取一个类型所有键值,返回一个联合类型

ts
type Person = { name: string; age: number; } type PersonKey = keyof Person; // PersonKey得到的类型为 'name' | 'age'

keyof 的一个典型用途是限制访问对象的 key 合法化,因为 any 做索引是不被接受的

ts
function getValue (p: Person, k: keyof Person) { return p[k]; // 如果k不如此定义,则无法以p[k]的代码格式通过编译 }

实例类型获取 typeof

typeof 是获取一个对象/实例的类型

ts
const me: Person = { name: 'gzx', age: 16 }; type P = typeof me; // { name: string, age: number | undefined } const you: typeof me = { name: 'mabaoguo', age: 69 } // 可以通过编译

遍历属性 in

in 只能用在类型的定义中,可以对枚举类型进行遍历

ts
// 这个类型可以将任何类型的键值转化成number类型 type TypeToNumber<T> = { [key in keyof T]: number }

keyof返回泛型 T 的所有键枚举类型,key是自定义的任何变量名,中间用in链接,外围用[]包裹起来(这个是固定搭配),冒号右侧number将所有的key定义为number类型。

于是可以这样使用了:

const obj: TypeToNumber = { name: 10, age: 10 }

总结起来 in 的语法格式如下:

[ 自定义变量名 in 枚举类型 ]: 类型

内置高级类型

Parameters

Parameters 用于提取函数类型的参数类型。

ts
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

类型参数 T 为待处理的类型,通过 extends 约束为函数,参数和返回值任意。 通过 extends 匹配一个模式类型,提取参数的类型到 infer 声明的局部变量 P 中返回。

image.png

ReturnType

ReturnType 用于提取函数类型的返回值类型

ts
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

类型参数 T 为待处理的类型,通过 extends 约束为函数类型,参数和返回值任意。

用 T 匹配一个模式类型,提取返回值的类型到 infer 声明的局部变量 R 里返回。

image.png

ConstructorParameters

构造器类型和函数类型的区别就是可以被 new。

Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

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

类型参数 T 是待处理的类型,通过 extends 约束为构造器类型,加个 abstract 代表不能直接被实例化(其实不加也行)。

用 T 匹配一个模式类型,提取参数的部分到 infer 声明的局部变量 P 里,返回 P。

image.png

InstanceType

提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

ts
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;

整体和 ConstructorParameters 差不多,只不过提取的不再是参数了,而是返回值。

通过模式匹配提取返回值的类型到 infer 声明的局部变量 R 里返回。

image.png

ThisParameterType

函数里可以调用 this,这个 this 的类型也可以约束

ts
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;

Partial

将泛型中全部属性变为可选

TS
type Partial<T> = { [P in keyof T]?: T[P] }
ts
type Animal = { name: string, category: string, age: number, eat: () => number } // 用Partial包裹一下 type PartOfAnimal = Partial<Animal>; const ww: PartOfAnimal = { name: 'ww' }; // 属性全部可选后,可以只赋值部分属性了

image.png

Required

作用:将类型 T 中所有的属性变为必选项。

ts
type Required<T> = { [P in keyof T]-?: T[P] }

这里有一个很有意思的语法-?,你可以理解为就是 TS 中把?可选属性减去的意思。

image.png

Readonly

将所有属性变为只读

ts
type Readonly<T> = { readonly [P in keyof T]: T[P]; };

image.png

Pick

映射类型的语法用于构造新的索引类型,在构造的过程中可以对索引和值做一些修改或过滤

ts
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };

类型参数 T 为待处理的类型,类型参数 K 为要过滤出的索引,通过 extends 约束为只能是 T 的索引的子集。

构造新的索引类型返回,索引取自 K,也就是 P in K,值则是它对应的原来的值,也就是 T[P]。

image.png

Record<K, T>

作用:将K中所有属性值转化为T类型,常用来声明一个普通object对象。

ts
type Record<K extends keyof any,T> = { [key in K]: T }

这里特别说明一下,keyof any对应的类型为number | string | symbol,也就是可以做对象键(专业说法叫索引 index)的类型集合。

image.png

举例如下

const obj: Record<string, string> = { 'name': 'zhangsan', 'tag': '打工人' }

image.png

image.png

Exclude<T, U>

作用:在 T 类型中,去除 T 类型和 U 类型的交集,返回剩余的部分。

当想从一个联合类型中去掉一部分类型时,可以用 Exclude 类型

ts
type Exclude<T, U> = T extends U ? never : T

注意这里的 extends 返回的 T 是原来的 T 中和 U 无交集的属性,而任何属性联合 never 都是自身

联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型

ts
type T0 = Exclude<"a" | "b" | "c", "a" | "f"> = "a" extends "a"|"f" ? never : "a" | //never "b" extends "a"|"f" ? never : "b" | //"b" "c" extends "a"|"f" ? never : "c" //"c" = "b"|"c"

Extract

可以过滤掉,自然也可以保留,Exclude 反过来就是 Extract,也就是取交集

ts
type Extract<T, U> = T extends U ? T : never;

image.png

Omit<T, K>

我们知道了 Pick 可以取出索引类型的一部分索引构造成新的索引类型,那反过来就是去掉这部分索引构造成新的索引类型。

作用:适用于键值对对象的 Exclude,它会去除类型 T 中包含 K 的键值对。

ts
type Omit = Pick<T, Exclude<keyof T, K>>

image.png

Awaited

获取多层嵌套Promise的返回值

ts
type Awaited<T> = T extends null | undefined ? T : T extends object & { then(onfulfilled: infer F): any } ? F extends ((value: infer V, ...args: any) => any) ? Awaited<V> : never : T;

类型参数 T 是待处理的类型。

如果 T 是 null 或者 undefined,就返回 T。

如果 T 是对象并且有 then 方法,那就提取 then 的参数,也就是 onfulfilled 函数的类型到 infer 声明的局部变量 F。

继续提取 onfullfilled 函数类型的第一个参数的类型,也就是 Promise 返回的值的类型到 infer 声明的局部变量 V。

递归的处理提取出来的 V,直到不再满足上面的条件。

image.png

Uppercase、Lowercase、Capitalize、Uncapitalize

这四个类型是分别实现大写、小写、首字母大写、去掉首字母大写的。

模式匹配

字符串

StartsWith

判断字符串是否以某个前缀开头

ts
type StartsWith<Str extends string, Prefix extends string> = Str extends `${Prefix}${string}` ? true : false;

eg: 16进制颜色值

ts
const color: `#${string}` = "#fff"
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:千寻

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!