使用
TypeScript
能够帮助我们使用很多面向对象的特性和类型系统。但是从JavaScript
迁移到TypeScript
也需要巨大的时间和精力去完成重构。但是VS Code
中基于TypeScript
提供了对于JSDoc
的支持,我们可以利用JSDoc
来给我们的JavaScript
代码增加注释,增强代码的可读性和可维护性。
什么是JSDoc?
首先我们来看一下官网上的介绍:
JSDoc 3 is an API documentation generator for JavaScript, similar to Javadoc or phpDocumentor. You add documentation comments directly to your source code, right alongside the code itself. The JSDoc tool will scan your source code and generate an HTML documentation website for you.
JSDoc ( JSDoc 的最新版本是JSDoc 3) 其实是通过特定格式的注释来帮助我们生成 JavaScript
API文档的工具。类似的工具还有 Javadoc
或 phpDocumentor
.
JSDoc可以做哪些注释?
JSDoc's purpose is to document the API of your JavaScript application or library. It is assumed that you will want to document things like modules, namespaces, classes, methods, method parameters, and so on.
JSDoc
的提供的注释标记类型可以参考Block Tags, 但是到现在为止,TypeScript
只支持了部分标记。
在语法方面,JSDoc
主要借鉴于 closure-compiler, 而 TypeScript
则有自己的一套类型语法,因此存在部分的差异。这里只介绍在 VS Code
中基于 TypeScript
的 JSDoc
用法。
定义对象类型——@type
能够在 TypeScript
中使用的类型有:
- 基本类型:
// 定义数组类型的三种方式
/**
* 定义字符串数组类型
* @type {Array<string>}
*/
var strArr = ['1', '2'];
// 以下是另外两种方式,作用和第一种一样
/**
* @type {Array.<string>}
*/
/**
* @type {string[]}
*/
/**
* 定义boolean类型
* @type {boolean}
*/
var isSelected = false;
/**
* 定义string类型
* @type {string}
*/
var name = 'xyz';
TypeScript
中添加的类型,比如:
/**
* 这是TypeScript定义的Navigator类型
* @type {Navigator}
*/
var navigator;
/**
* 这是TypeScript定义的窗口类型
* @type {Window}
*/
var win;
/**
* 这是TypeScript定义的HTML元素类型
* @type {HTMLElement}
*/
var rootElement = document.getElementById('root');
- 大部分的JSDoc类型
- 对象类型:
/**
* 定义了一个Animal类型,具有名称、年龄两个属性
* @type {{ name: string, age: number }}
*/
var cat = { name: 'miao', age: 2 };
- 联合类型:适合于可能拥有多种类型之一的数据类型,该类型可以从提供的类型列表中确定
// 这是一个字符串类型/布尔类型
/**
* 用来描述一个可能是string或boolean的类型
* @type {(string | boolean)}
*/
var stringOrBoolean = '';
stringOrBoolean = false;
- 定义函数类型
/**
* 用闭包的形式来定义函数类型
* @type {function(string, boolean): number}
*/
var function1;
/**
* 用TypeScript的语法来定义函数类型
* @type {(s: string, b: boolean) => number}
*/
var function2;
/**
* 使用内置的Function类型,不指定参数和返回值
* @type {Function}
*/
var function 3;
定义自定义类型和属性——@typedef和@property/@prop
- 如果要创建一些在JSDoc中使用的复杂类型,就需要用@typedef去定义了这个类型
- 然后使用@property或@prop去定义类型的属性。
- 定义完成后,通过@type去使用自行定义的类型:
/**
* @typedef {object} Animal - 这是自定义的Animal类型
* @property {string} name 复杂类型的一个string类型属性
* @property {number} age 复杂类型的一个number类型属性
* @prop {boolean} [hasOwner] 复杂类型的一个boolean类型属性,可选
* @property {string[]=} toys 复杂类型的一个string数组属性,可选
* @prop {string} [ownerName='xyz'] 复杂类型的一个string类型属性,默认值为xyz, 可选
*
*/
/**
* @type {Animal}
*/
var animal = { name: 'miao', age: 2, hasOwner: false }
定义回调函数——@callback
@callback类似于@typedef,区别在于: @typedef定义的是对象类型,包括函数类型,而@callback定义的是函数类型。
/**
* @callback Predicate
* @param {string} data
* @param {number} [index]
* @returns {boolean}
*/
/** @type {Predicate} */
const ok = s => !(s.length % 2);
定义函数参数和返回值——@param/@arg/@arguments和@return
- @param/@arg/@arguments用来定义函数参数,使用了和@type相同的类型语法
- @return用来定义函数的返回值。关于参数的定义方式和@typedef相似。
/**
* @param {string} name - string类型参数
* @param {string=} age - 可选参数,number类型
* @param {number} [hasOwner] - 可选参数,number类型
* @param {string} [ownerName="xyz"] - 带默认值的可选参数
* @return {string} 这是返回值
*/
function getAnimal(name, age, hasOwner, ownerName){
// TODO
}
定义枚举——@enum
枚举是一种在很多种语言中常见,但是在JavaScript中没有原生支持的数据类型。但是最让人残念的是,enum
居然还是保留的关键字——只是一直没有实现。
TypeScript是支持enum的,JSDoc也基于@enum提供了对枚举的支持:
/**
* 文件类型
* @enum
* @property {string} FileType.Image 图片
* @property {string} FileType.Video 视频
* @property {string} FileType.Audio 音频
* @property {string} FileType.Accessory 附件
*/
export const FileType = {
/** 图片 */
Image: '1',
/** 视频 */
Video: '2',
/** 音频 */
Audio: '3',
/** 附件 */
Accessory: '4'
}
定义构造器——@class/@constructor
@class/@constructor可以用来定义构造函数,并且只允许通过 new
关键字来调用构造函数。
/**
* @constructor
* @param {number} width 宽度
* @param {number} height 高度
*/
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.getArea = function() {
return this.width * this.height
}
// 必须要通过new来调用,不然编辑器会报错
var rectrangele = new Rectangle();
定义this指向——@this
VS Code可以帮我们做一些类型推断。但是有一些场景没法很好的做出推断,通过使用@this可以帮助我们来显式指定 this
的类型。
/**
* @this {HTMLElement}
*/
function getScrollbarWidth() {
return this.offsetWidth - this.scrollWidth
}
其他
@type——关于Nullable和Non-nullable
JSDoc中关于@type有 Nullable Type
和 Non-nullable Type
的概念,但是在TypeScript中只允许根据 strictNullChecks
标记类型为是否可以为null,并不能通过显示标注 non-nullablity
来实现和JSDoc的一致,例如:
/**
* 在JSDoc中标记为可能为number或null类型
* @type {?number}
*/
/**
* 在JSDoc中标记为number类型,并且不可能为null类型
* @type {!number}
*/
/**
* 以上两种写法,在TypeScript下都等价于如下
* @type {number}
*/
在TypeScript中导入其他文件中的定义
这是在TypeScript中独有的,JSDoc并不支持
// a.js
/**
* @typedef Pet
* @property name {string}
*/
/**
* @type {Pet}
*/
var cat = { name: 'a' };
module.exports = {
// cat
};
// b.js
/**
* @param p {import('./a').Pet}
*/
function walk(p) {
console.log(`${p.name} is walking!`)
}
// 也可以给引入的类型起别名
/**
* @typedef { import('./a').Pet } MyPet
*/
/**
* @type {MyPet}
*/
var mimi = { name: 'mimi' }
定义泛型——@template
这是JSDoc文档中没有提及的一种定义类型,只在google closure compiler中有提及,但是VS Code中有对应的支持:
/**
* @param {T} x
* @param {S} y
* @template {number|string} T
* @template {number|string} S
*/
function foo(x,y) { x=y; }
定义扩展泛型基类——@extends/@augments
这几项用的都不多,我们可以看看官方文档的一些示例:
/**
* @template T
* @extends {Set<T>}
*/
class SortableSet extends Set {
// ...
}
需要注意的是@extends/@augments只能用来定义基类的泛型参数,不能用来描述类的继承关系:
class Animal {
run() {
}
}
/**
* @extends {Animal}
*/
class Cat { }
var cat = new Cat()
// 这里会没有代码提示
cat.run()