Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术随笔

    • git教程
    • 前端模块化:CommonJS,AMD,CMD,ES6
    • 如何动态加载本地图片
    • GitHub自动部署脚本
    • React生命周期详解
    • 将父组件的props作为组件的初始state的几种方案
    • webpack基础入门
    • StackOver记录:如何在function中使用redux
    • React中使用onClick时的三种性能优化
    • 思考React状态管理
    • forEach/for..in/of
    • styled-components使用记录
    • 深入理解forEach与forof
    • Node技术架构
    • Redux、Mobx状态管理杂谈
    • JS中的类型检测
    • 如何判断图片是否在可视区域
    • 如何使用图片的懒加载操作
    • JavaScript模块化:从CommonJS到AMD
    • git查看:修改用户名、密码
    • 保持数据Immutable的几种写法
    • 如何实现一个Promise.all
    • resolve(Promise对象)会多出两个微任务
    • todo_基于Mocha的测试驱动开发
    • 浏览器页面渲染机制
    • todo-响应式编程之数据劫持
    • todo-immer库的介绍
    • React-Hooks 基础原理解析
    • JS基本功
    • 函数闭包与this指针
    • 收集 DUMI 配置过程中遇到的问题
    • typescript 类型工具
      • 0. 前言
      • 1. 什么是工具类型
      • 2. Ts 中内置工具类型
        • 0. 函数类型相关
        • 1. 类型相关
        • 2. 对象相关
        • 3.Promise 类型
      • 3. 自定义工具类型
        • Maybe + 类型
        • 解决裸参数问题
    • 统一接口的导入导出
    • 网络安全:如何预防 XSS 攻击
    • 浏览器页面渲染机制【2023】
    • 跨域资源请求
    • 浏览器事件循环机制
    • 如何理解浏览器的 user agent 用户代理的含义?
  • 转载文章

  • Mac相关

  • 前端学习周报

  • 包管理工具

  • 技术随笔
  • 技术随笔
wangjiasheng
2022-11-27
目录

typescript 类型工具

# 0. 前言

本篇博客主要是对 Typescript 内置类型做一个梳理和总结。

TypeScript 官网内置类型工具见:https://www.typescriptlang.org/docs/handbook/utility-types.html

# 1. 什么是工具类型

在 ts 中,我们可以使用 type 或者 interface 进行类型约束,这些类型一旦写死,后期只能通过 & 或者 | 的方式进行扩展,而借助工具类型可实现更灵活的类型创建功能。

我们可以将工具类型类比函数:

  • 真实的函数:入参和出参都是具体的值。

  • 工具类型:以泛型的方式传入类型,返回结果是一个新的类型。

    如,下面这个例子可在原有的 string 和 number数据类型基础上,再支持一种类型。

    type Factory<T> = T | number | string;
    
    1

在 Typescript 内置了许多的工具类型,以实现常见类型的转换。并且这些工具类型是作用在全局的,可以直接使用。但是有一点,在使用时需确定注意 ts 版本。

注:我们可以在全局直接使用的原因,内置工具类型被定义在 node_modules/typescripts/lib/lib.es5.d.ts 文件中。

# 2. Ts 中内置工具类型

# 0. 函数类型相关

  • Parameters(v3.1)
  • ReturnType(v2.8)

如果存在如下函数:

function add(num1:number,num2:number)=>{
  return num1 + num2;
}
1
2
3

通过内置类型,我们可以:

// 1. 通过 typeof 获取 function 的 ts 定义
type addFunType = typeof add; //  (num1:number,num2:number) => number

// 2. 通过 Parameters 获取入参类型
type InputType = Parameters<typeof fun>[0];

// 3. 通过 ReturnType 获取出参类型
type outputType = ReturnType<typeof fun>;
1
2
3
4
5
6
7
8

此类函数在 React 组件中非常有用,无论是 Class Component 还是 Function Component 本质上都是一个函数,通过 Parameters 可以快速拿到组件的 props 类型,如下:

// 提取 ComponentA 组件的 PropsType
type ComponetAPropsType = Parameters<typeof ComponentA>[0];

// 有了此方法,定义 HOC 就不必再重复写一次 Props 的类型,而是可以通过组件自身推断出来。
const ComponentAWithHeader = AddHeaderHoc<ComponetAPropsType>(ComponentA);
1
2
3
4
5

此技巧来源于:字节跳动:Larry (opens new window)

不使用工具类型的写法如下:

type Parameters<K extends (...args:any) => any> = K extends(...args: infer P) =>any => any ? P : never
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
1
2

核心原理:使用到了 infer + extends(类型判断)

# 1. 类型相关

  • Extract<Type, Union> (2.8):提取公共类型
  • Exclude<UnionType, ExcludedMembers>(2.8):排除指定类型

使用说明如下,如存在一个联合对象:

type objType = string | number | (() => void);
1

通过内置类型,我们可以做到

// 1. 提取公共类型, 如提取出所有的function
type T0 = Extract<string | number | (() => void), Function>;

// 2. 或者排除掉 string 和 number 类型
type T0 = Exclude<string | number | (() => void,string | number>;
1
2
3
4
5

尽管使用方式非常简单,但是底层实现机制还是有一定难度的,需要对 extends 这个关键字的原理非常清楚,代码也非常少:

type Extract<T, U> = T extends U ? T : never;
type Exclude<T, U> = T extends U ? never : T;
1
2

在条件类型中会使用 extends 去判断类型的兼容性,但是会存在一个细节点,先看下面的这个例子:

type Condition<T> = T extends 1 | 2 | 3 ? T : never;

// 直接判断,结果显然是 never 没啥问题。
type Res2 = 1 | 2 | 3 | 4 | 5 extends 1 | 2 | 3 ? 1 | 2 | 3 | 4 | 5 : never;

// 1 | 2 | 3 (联合类型这里会实现了一个广播的效果)
type Res1 = Condition<1 | 2 | 3 | 4 | 5>;
1
2
3
4
5
6
7

当 联合类型 以泛型参数的方式传入时,原有的条件类型判断会发生改变,不再是对两个类型进行直接判断(前者是否兼容后者),而是以一种广播(automativally distributed)的方式对联合类型逐个进行比较,并将结果进行合并。即输入联合,输出联合。

PS:官方的说法是,这是条件类型的分布式特性,并且定义了一个裸类型参数的概念。

Conditional types in which the checked type is a naked type parameter are called distributive conditional types. Distributive conditional types are automatically distributed over union types during instantiation.

因此这里的内置类型也是利用到的 extend + Union Type 的 auto distribute 特性。

如果禁用此特性,可以使用以下方案:

// 方案1:
type NoDistribute<T> = T & {};
// 方案2:使用数组包裹
type CompareUnion<T, U> = [T] extends [U] ? true : false;
1
2
3
4

# 2. 对象相关

  • Record<Keys,Type>(v2.8)
  • Omit<Type, Keys> (3.5)
  • Pick<Type, Keys> (2.1)
  • Partial<Type> (2.1)
  • Required<Type> (2.8)
  • Readonly<Type> (2.1)
  • 自定义属性 clone

对于对象内置工具类型而言,通过简单的类型映射就可以实现,内置更多的是一种简化操作。

使用说明,当存在一个对象如下:

interface Todo {
  title: string;
  description?: string;
  completed: boolean;
  createdAt: number;
}
1
2
3
4
5
6

通过内置类型,我们可以做到:

// 1. 将 Todo 的所有属性设置为必选,或者可选
type T0 = Required<Todo>;
type T1 = Partial<Todo>;
type T3 = ReadOnly<Todo>;

// 2. 筛选或者丢弃掉多个属性
type T3 = Pick<Todo, "title" | "description">;
type T4 = Omit<Todo, "title" | "description">;

// 3. 使用 record 创建对象
type Todo2 = Record<"title" | "description", string> &
  Record<"completed", boolean> &
  Record<"createdAt", number>;

// 4. 提取 key 与 value 的联合类型
type keyType = keyof Todo; // "title" | "description" | "completed" | "createAt"
type valueType = Todo[keyof Todo]; // string | number | boolean | undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

各自内部的代码实现,核心是 keyof 提取出索引签名 实现的:

// keyof 是联合变量,联合变量在 ts 中可通过 [ P in 联合类型 ] 的形式遍历
// 与 extends 关键字类型,此处联合类型会被自动分发

/* 假设我们实现一个 clone 接口,用于拷贝一个 Ts 类型*/
type clone<T> = {
  [P in keyof T]: T[P];
};

/* 只需在 clone 基础上稍作修改可以封装出 Partial 和 Require 类型*/
type Required<T> = {
  [P in keyof T]-?: T[P];
};
type Partial<T> = {
  [P in keyof T]?: T[P];
};

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>>;

/* Record 就是上面的简化版*/
type Record<K extends keyof any, T> = {
  [P in K]: T;
};
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

# 3.Promise 类型

在 Typescript 的 4.5 版本中,新增了 Awaited<Type> 类型

使用方案如下:

type A = Awaited<Promise<string>>;
// 即使嵌套也可以使用
type B = Awaited<Promise<Promise<string>>>; // string
// 联合类型也适用
type C = Awaited<boolean | Promise<number>>; // number | boolean
1
2
3
4
5

核心原理如下:(没看懂)

type Awaited<T> = T extends null | undefined
  ? T // special case for `null | undefined` when not in `--strictNullChecks` mode
  : T extends object & { then(onfulfilled: infer F): any } // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
  ? F extends (value: infer V, ...args: any) => any // if the argument to `then` is callable, extracts the first argument
    ? Awaited<V> // recursively unwrap the value
    : never // the argument to `then` was not callable
  : T; // non-object or non-thenable
1
2
3
4
5
6
7

# 3. 自定义工具类型

# Maybe + 类型

// 扩展类型
type Factory<T> = T | number | string;

// 有可能为 null
type MaybeNull<T> = T | null;

// 有可能为 Array
type MaybeArray<T> = T | T[];
1
2
3
4
5
6
7
8

# 解决裸参数问题

  • NoDistribute
  • CompareUnion:在泛型中比较联合类型
// 禁止联合类型自动分发
type NoDistribute<T> = T & {};

// 比较两个类型
type CompareUnion<T, U> = [T] extends [U] ? true : false;
1
2
3
4
5

测试如下:

// 测试1:
type Wrapped<T> = NoDistribute<T> extends boolean ? "Y" : "N";
type Res1 = Wrapped<number | boolean>; // "N"
type Res2 = Wrapped<true | false>; // "Y"
type Res3 = Wrapped<true | false | 599>; // "N"

// 测试2:
type CompareRes1 = CompareUnion<1 | 2, 1 | 2 | 3>; // true
type CompareRes2 = CompareUnion<1 | 2, 1>; // false
1
2
3
4
5
6
7
8
9
编辑 (opens new window)
上次更新: 2022/12/20, 22:12:00
收集 DUMI 配置过程中遇到的问题
统一接口的导入导出

← 收集 DUMI 配置过程中遇到的问题 统一接口的导入导出→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式