玩转TypeScript工具类型(中)
文章图片
【玩转TypeScript工具类型(中)】联系我们:有道技术团队助手:ydtech01 / 邮箱:ydtech@rd.netease.com
本文是《玩转TypeScript工具类型》系列的第二篇,包含了如下几部分内容:
- 必读:extends条件运算符
- Exclude
- Extract
- NonNullable
- 必读:tuple type元组类型
- Parameters
- ConstructorParameters
- ReturnType
- InstanceType
二. Exclude
2.1 源码解读
type Exclude = T extends U ? never : T;
如何理解 T extends U ? never : T 呢?判断T中的每一项是否可以赋值给类型U,如果可以,就返回never,如果不可以,就返回当前这项。
接下来我们结合一个具体的例子来理解一下这句话。
2.2 实战用法
// 排除一个具体的值type T0 = Exclude<"a" | "b" | "c", "a">// "b" | "c"// 排除一种类型type T1 = Exclude void), Function>
以 T0 为例,extends 关键字是如何发挥作用的呢?
type T0 = Exclude<"a" | "b" | "c", "a">// "b" | "c"// 等价于type T0 = "a" extends "a" ? never : "a" | "b" extends "a" ? never : "b"| "c" extends "a" ? never : "c"// 等价于type T0 = never | "b" | "c"// 等价于type T0 = "b" | "c"
如果无法理解这里的等价逻辑,建议阅读开头推荐的[[[译]TypeScript条件类型]
](https://juejin.cn/post/698546...)
三. Extract
3.1 源码解析
/** * Extract from T those types that are assignable to U */type Extract = T extends U ? T : never;
提取出类型 T 中那些能赋值给类型 U 的类型。所以这里源码上和 Exclude 的区别就在于 never 放在了条件运算结果为 false 的分支上。
所以如果理解了 Exclude ,再理解 Extract 就不复杂了。
3.2 实战用法
// 提取一个具体的值type T0 = Extract<"a" | "b" | "c", "a">// "b" | "c"// 提取一种类型type T1 = Extract void), Function>
这里就不再重复说明 extends 的处理逻辑了。
四. NonNullable
4.1 源码解析
type NonNullable = T extends null | undefined ? never : T
可以发现和 Exclude
4.2 实战用法
type T0 = NonNullable;
//type T0 = string | numbertype T1 = NonNullable;
//type T1 = string[]
五. 必读:tuple type元组类型 元组类型就是一个具有固定数量元素和元素类型都确定的数组类型。
Tuple types allow you to express an array with a fixed number of elements whose types are known, but need not be the same.例如这就是一个元组类型:
let x: [string, number];
当我们访问已声明类型的元素时,可以获得正确的类型检查:
// OKconsole.log(x[0].substring(1));
// Property 'substring' does not exist on type 'number'.console.log(x[1].substring(1));
当我们访问超出数组长度的下标时,获得的类型都是 undefined ,并且会获得一个访问下标无可访问元素的错误提示:
let x: [string, number];
x = ["hello", 10];
// OK// Type '"world"' is not assignable to type 'undefined'.// Tuple type '[string, number]' of length '2' has no element at index '3'.x[3] = "world";
// Object is possibly 'undefined'.// Tuple type '[string, number]' of length '2' has no element at index '5'.console.log(x[5].toString());
六. Parameters
6.1 源码解析
/** * Obtain the parameters of a function type in a tuple */type Parameters any> = T extends (...args: infer P) => any ? P : never;
源码中重点有两部分:
- T extends (...args: any) => any ,规定了 T 必须是一个函数( any 和 never除外),参数是 any 类型,所以参数可以是任意类型。
- T extends (...args: infer P) => any ? P : never,如果 T 是函数类型,那么这个 extends 走的就是 true 分支,也就是返回一个 P 类型,而这个 P 类型就是参数 args 的类型
6.2 实战用法
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<(arg: T) => T>;
//type T2 = [arg: unknown]type T3 = Parameters;
//type T3 = [arg: { a: number;
b: string;
}]type T4 = Parameters;
//type T4 = unknown[]type T5 = Parameters;
//type T5 = nevertype T6 = Parameters;
//Type 'string' does not satisfy the constraint '(...args: any) => any'.type T7 = Parameters;
//Type 'Function' does not satisfy the constraint '(...args: any) => any'.//Type 'Function' provides no match for the signature '(...args: any): any'.
这里需要关注一下 Parameters 传入 never 和 any 的情况,知道即可。
七. ConstructorParameters
7.1 源码解析
/** * Obtain the parameters of a constructor function type in a tuple */type ConstructorParameters any> = T extends abstract new (...args: infer P) => any ? P : never;
这里有两个需要关注的点:
- abstract关键字修饰的函数叫抽象方法,而抽象方法只能出现在抽象类中
- new (...args: any) => any,这就是对构造函数的定义
7.2 实战用法
这里结合一个具体的例子,深入了解一下,就以 ErrorConstructor 为例:
interface ErrorConstructor {new(message?: string): Error;
(message?: string): Error;
readonly prototype: Error;
}type T0 = ConstructorParameters;
这里 ConstructorParameters 是如何处理的呢?其实就是对 ErrorConstructor 的三条属性逐一代入 T extends abstract new (...args: infer P) => any ? P : never; ,满足条件的就返回参数类型 P。
type T0 = string
八. ReturnType
8.1 源码解析
/** * Obtain the return type of a function type */type ReturnType any> = T extends (...args: any) => infer R ? R : any;
和 Parameters
注意返回值类型不是元组类型,因为在 Parameters
8.2 实战用法
declare function f1(): { a: number;
b: string };
type T0 = ReturnType<() => string>;
//type T0 = stringtype T1 = ReturnType<(s: string) => void>;
//type T1 = voidtype T2 = ReturnType<() => T>;
//type T2 = unknowntype T3 = ReturnType<() => T>;
//type T3 = number[]type T4 = ReturnType;
//type T4 = {a: number;
b: string;
}type T5 = ReturnType;
//type T5 = anytype T6 = ReturnType;
//type T6 = nevertype T7 = ReturnType;
//类型“string”不满足约束“(...args: any) => any”。ts(2344)type T8 = ReturnType;
//类型“Function”不满足约束“(...args: any) => any”。//类型“Function”提供的内容与签名“(...args: any): any”不匹配。ts(2344)
九. InstanceType
new
操作符后的返回值类型。我们已经知道了如何获取构造函数参数的类型,所以很方便的就可以推导出如何获取实例的类型。9.1 源码解析
/** * Obtain the return type of a constructor function type */type InstanceType any> = T extends abstract new (...args: any) => infer R ? R : any;
也是把 infer 的位置从参数位置移到了返回值位置。
9.2 实战用法
这里我们继续以 FunctionConstructor 为例:
interface FunctionConstructor {/*** Creates a new function.* @param args A list of arguments the function accepts.*/new(...args: string[]): Function;
(...args: string[]): Function;
readonly prototype: Function;
}type T0 = InstanceType;
结合 new(...args: string[]): Function; 可知:
type T0 = Function;
更多情况可以在 typescript playground 中自己尝试一下。
十. 下回预告 在下一篇《玩转TypeScript工具类型(下)》里,将会包括如下内容,敬请期待:
ThisParameterType
OmitThisParameter
ThisType
快捷跳转
- 玩转TypeScript工具类型(上)
推荐阅读
- 标签、语法规范、内联框架、超链接、CSS的编写位置、CSS语法、开发工具、块和内联、常用选择器、后代元素选择器、伪类、伪元素。
- 孩子不是实现父母欲望的工具——林哈夫
- 最有效的时间管理工具(赢效率手册和总结笔记)
- 工具|后天就是七夕节,你准备好了吗(送上几个七夕代码,展示你技能的时候到了!)
- 工具分享|5个有趣好玩的网站,拒绝无聊!
- typeScript入门基础介绍
- 工具人的一天
- 炒股知识(超级短线操作的秘籍|炒股知识:超级短线操作的秘籍 玩转股市)
- jvm常见分析工具
- docker镜像探索----dive工具