前言


当 TypeScript 越来越流行时,许多项目选择运用 TypeScript 进行开发,由于它带来了许多优点,这一点咱们都有所耳闻。
然而,一些旧项目依然采用传统的 JavaScript 技能,由于进行全面重构成本太高。
在这种情况下,咱们就需要介绍 JSDoc(也称为文档注释)作为一种解决方案。

JSDoc 是一种在 JavaScript 中运用注释来供给类型信息和文档阐明的办法。 JSDoc 不仅能够供给类型提示,还能够生成文档,协助开发人员更好地了解代码。
JSDoc 的出现为传统 JavaScript 项目引入了一种新的方式,使得即便在没有 TypeScript 的情况下,也能够获得一些相似 TypeScript 的优点。

重新看待JSDoc


信任咱们都曾见过下面这样的写法,经过运用 @param 标签符号函数参数的名称、类型和描绘,运用 @returns 标签符号函数回来值

/**
 * 方针兼并,假如存在相同的特点,优先运用 target 的特点.
 */
function merge(source, target) { ... }
/**
 * 方针兼并,假如存在相同的特点,优先运用 target 的特点.
 * @param { Object } source 源方针
 * @param { Object } target 方针方针
 * @returns { Object } 回来兼并后的新方针
 */
function merge(source, target) { ... }

但假如是以下这样呢,是不是就有点TS的滋味了(手动狗头)

/**
 * 方针兼并,假如存在相同的特点,优先运用 target 的特点
 * @template { Record<string, any> } T
 * @template { Record<string, any> } U
 * @param { T } source 源方针
 * @param { U } target 方针方针
 * @returns { Omit<T, keyof (T | U)> & U } 回来兼并后的新方针
 */
function merge(source, target) { ... }
const test = merge({ name: 'test', gender: '男' }, { age: 18, gender: 1 })

JSDoc的进阶运用

能够在 JSDoc 中运用 TS 语法,这样的话,就算在 JS 开发的项目中,“体操哥”不也有发挥空间了嘛。
假定你现已具备了一定的TS水平,那接下来让咱们直接进入正题。假如你不太了解甚至为泛型而感到头疼,《TS类型的进阶运用 – 》这篇文章希望能够帮到你。

泛型


当谈到 TS 时,最具挑战性的部分莫过于泛型。泛型能够说是 TS 的精华,它为咱们供给了在编写灵敏且类型安全的代码时所需的强大东西。泛型使得咱们能够以一种抽象的方式来处理不同类型的数据,然后提高了代码的灵敏性和可维护性

在 JSDoc 中运用 @template 标签声明泛型

/**
 * @template T 声明泛型
 * @param { T } value 入参
 * @returns { T } 回来值
 */
function identity(value) { } // function identity<T>(value: T): T
const a = identity("text"); // string
const b = identity(123); // number

JSDoc的进阶运用

能够从提示中看到 identity 现已变成了一个规范的泛型函数了,传入什么类型回来什么类型

运用逗号或多个标签声明多个泛型参数:

/**
 * @template T,U,V
 * @template W,X
 */

还能够在泛型名称之前进行泛型约束

/**
 * @template { unknown[] } T
 * @param { T } value 要获取长度的数组
 * @returns { T['length'] } 数组的长度
 */

JSDoc的进阶运用

能够看到这些满是数组的办法和特点,编辑器现已能够识别出你的 T 是一个数组了

最终,还能够为泛型设置默许类型

/**
 * @template [T = string] 声明泛型并设置默许类型为string
 * @param { T } value 入参
 * @returns { T } 回来值
 */
function identity(value = '') { }

JSDoc的进阶运用

omit函数

结合之前所讲的泛型,来看看一个经典的函数——omit

/**
 * omit函数 去除方针中的值得特点
 * @template { Record<string, any> } T 方针类型
 * @template { Array<keyof T> } K 元组类型
 * @param { T } target 方针元素
 * @param { K } keys 特点
 * @returns { Omit<T, K[number]> } 新方针
 */
function omit(target, keys) { ... }

作用:

  1. JSDoc的进阶运用
  2. JSDoc的进阶运用

总结:

  1. 能得到作用1,归功于 @template { Array<keyof T> } K 元组类型 这段代码将泛型 K 的类型约束成 (name | gender)[] ,使元组中的每个方位都是泛型 T 方针类型上的特点。
  2. 作用2则归功于多个泛型参数以及泛型约束,K[number] 将元组转成联合类型,假定函数的形参keys[name, gender]那么将转换为name | gender,所以图中的转换为 [name]=> name;然后再结合 Omit 即可达到咱们想要的作用了。能够拿到 keys 具体的类型十分要害,想想假如只要一个泛型参数能够做到吗?

杂乱类型


在前面,咱们所评论的类型中,都是一些简略的类型,一行代码就能够搞定的,假如某个函数的类型特别杂乱,需要写许多代码,这时候又该怎么应对呢?

类型导入

运用 import 从其他文件中导入类型

// 文件:utils.d.ts
// 阐明:过滤掉方针中特点值为指定内容的特点,运用 as 要害字来进行过滤
export type RemovePropertiesByValue<T extends Record<string, any>, V extends readonly unknown[]> = {
    [K in keyof T as T[K] extends V[number] ? never : K]: T[K]
}
// 文件:JSDoc.js
/**
 * 去除方针中特点值为空的特点
 * @template { Record<string, any> } T
 * @template { readonly any[] } [V = [undefined, null]]
 * @param { T } target 要处理的方针
 * @param { V } values 自定义过滤的value值,默许为[undefined, null]
 * @returns { import("./utils").RemovePropertiesByValue<T, V> } 处理后的方针
 *            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 */
function filterObjectEmpty(target, values = [undefined, null]) { }
const test4 = filterObjectEmpty({ name: undefined, age: 18, gender: '', address: null }) // 测试代码

JSDoc的进阶运用

咱们从 utils.d.ts 类型文件中运用import导入了 RemovePropertiesByValue 类型,此类型接收两个泛型参数。
@returns { import("./utils").RemovePropertiesByValue<T, V> } 导入了此类型并将 泛型T 和 泛型V 作为参数传递给 RemovePropertiesByValue

既然如此,还有什么类型是无法用 JSDoc 处理的呢,我想只要咱们无法写的类型吧 本文的要点(泛型)现已讲完,假如你对 JSDoc 现已有了爱好,能够去具体看看官方文档

参考资料


  1. Documentation – JSDoc Reference
  2. 块级标签 | JSDoc中文文档 | JSDoc中文网