那些你忽略的typescript小技巧
?? 与 || 功能很相似,不同的是 ??在左侧表达式为 null 或 undefined 时,才会右侧表达式。
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 的
jsfunction 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 可以获取一个类型所有键值,返回一个联合类型
tstype Person = {
name: string;
age: number;
}
type PersonKey = keyof Person;
// PersonKey得到的类型为 'name' | 'age'
keyof 的一个典型用途是限制访问对象的 key 合法化,因为 any 做索引是不被接受的
tsfunction getValue (p: Person, k: keyof Person) {
return p[k]; // 如果k不如此定义,则无法以p[k]的代码格式通过编译
}
typeof 是获取一个对象/实例的类型
tsconst me: Person = { name: 'gzx', age: 16 };
type P = typeof me; // { name: string, age: number | undefined }
const you: typeof me = { name: 'mabaoguo', age: 69 } // 可以通过编译
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 用于提取函数类型的参数类型。
tstype Parameters<T extends (...args: any) => any>
= T extends (...args: infer P) => any
? P
: never;
类型参数 T 为待处理的类型,通过 extends 约束为函数,参数和返回值任意。 通过 extends 匹配一个模式类型,提取参数的类型到 infer 声明的局部变量 P 中返回。

ReturnType 用于提取函数类型的返回值类型
tstype ReturnType<T extends (...args: any) => any>
= T extends (...args: any) => infer R
? R
: any;
类型参数 T 为待处理的类型,通过 extends 约束为函数类型,参数和返回值任意。
用 T 匹配一个模式类型,提取返回值的类型到 infer 声明的局部变量 R 里返回。

构造器类型和函数类型的区别就是可以被 new。
Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。
tstype 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。

提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。
tstype InstanceType<T extends abstract new (...args: any) => any>
= T extends abstract new (...args: any) => infer R
? R
: any;
整体和 ConstructorParameters 差不多,只不过提取的不再是参数了,而是返回值。
通过模式匹配提取返回值的类型到 infer 声明的局部变量 R 里返回。

函数里可以调用 this,这个 this 的类型也可以约束
ts type ThisParameterType<T> =
T extends (this: infer U, ...args: any[]) => any
? U
: unknown;
将泛型中全部属性变为可选
TStype 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' }; // 属性全部可选后,可以只赋值部分属性了

作用:将类型 T 中所有的属性变为必选项。
tstype Required<T> = {
[P in keyof T]-?: T[P]
}
这里有一个很有意思的语法-?,你可以理解为就是 TS 中把?可选属性减去的意思。

将所有属性变为只读
ts type Readonly<T> = {
readonly [P in keyof T]: T[P];
};

映射类型的语法用于构造新的索引类型,在构造的过程中可以对索引和值做一些修改或过滤
tstype Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
类型参数 T 为待处理的类型,类型参数 K 为要过滤出的索引,通过 extends 约束为只能是 T 的索引的子集。
构造新的索引类型返回,索引取自 K,也就是 P in K,值则是它对应的原来的值,也就是 T[P]。

作用:将K中所有属性值转化为T类型,常用来声明一个普通object对象。
ts type Record<K extends keyof any,T> = {
[key in K]: T
}
这里特别说明一下,keyof any对应的类型为number | string | symbol,也就是可以做对象键(专业说法叫索引 index)的类型集合。

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


作用:在 T 类型中,去除 T 类型和 U 类型的交集,返回剩余的部分。
当想从一个联合类型中去掉一部分类型时,可以用 Exclude 类型
tstype 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"
可以过滤掉,自然也可以保留,Exclude 反过来就是 Extract,也就是取交集
tstype Extract<T, U> = T extends U ? T : never;

我们知道了 Pick 可以取出索引类型的一部分索引构造成新的索引类型,那反过来就是去掉这部分索引构造成新的索引类型。
作用:适用于键值对对象的 Exclude,它会去除类型 T 中包含 K 的键值对。
tstype Omit = Pick<T, Exclude<keyof T, K>>

获取多层嵌套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,直到不再满足上面的条件。

这四个类型是分别实现大写、小写、首字母大写、去掉首字母大写的。
判断字符串是否以某个前缀开头
ts type StartsWith<Str extends string, Prefix extends string> =
Str extends `${Prefix}${string}` ? true : false;
eg: 16进制颜色值
ts const color: `#${string}` = "#fff"


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