使用 JSDoc 标注类型

简介: JavaScript 采用动态类型,在编译期不对类型进行检查,等真正执行的时候由运行时来判断类型是否出现错误,这种特性有点像汇编里的计算一样,类型很弱,不同类型总是显式、隐式地转换。

使用 JSDoc 标注类型

JavaScript 采用动态类型,在编译期不对类型进行检查,等真正执行的时候由运行时来判断类型是否出现错误,这种特性有点像汇编里的计算一样,类型很弱,不同类型总是显式、隐式地转换。

在一个普通的 Todo Demo 里类型或许没有那么重要,而当项目变得庞大起来的时候,里边有很多不同的逻辑,当项目由另外一个人接手的时候,阅读就很麻烦,因为总是要查看代码上下文的各个变量的类型(自定义类型或者原生类型),一般来说,在前端 console.log 一下就可以看到了,但也只是一个临时的办法,治标不治本。

当然,强类型的 JavaScript 已经很成熟了,那就是 TypeScript,但并不是所有项目一开始就是 TS 写的,也有纯 JS 的项目,那么在这些项目里如何维护好类型?

答案是在 VSCode 下使用 JSDoc;VSCode 会尽最大努力推导出类型出来的。

什么是 JSDoc

JSDoc 其实就是代码注释,在这种注释里可以标明 JavaScript 里值的类型,以及对代码的注释,通常可以在代码里看到以 @ 符开头的标注,那就是 JSDoc 了。以下是一个简单的例子

/**
 * 加法
 * @param { Number } x 
 * @param { Number } y 
 * @returns { Number } 
 */
function add(x, y) {
    return x + y; 
}
复制代码

上面 @param@returns 就说明了参数 x y 以及函数的返回值的类型。

参考 VSCode - type-checking 可以显式的打开 JS 的类型检查:

打开类型检查可以看到传入其他类型的参数的时候会报错

打开类型检查可以看到传入其他类型的参数的时候会报错,而填入了正确的类型的值之后,VSCode 就能知道 z 是什么类型的了(因为 x, y 均为数字,x + y 也理所当然的是数字)

也就能实现代码补全提示了:

代码补全

标注代码里的基本类型

@param 后可以接 String Number Array Object 等等。 如:

/**
 * 
 * @param { Array<Number> } numbers
 * @param { String[] } names
 */
function test(numbers, names) {
    names.forEach(name => {
        // ... 
    })
}
复制代码

上面的 String[] 的写法其实与 Array<String> 是一样的,表示了一个元素为字符串的数组,而且 VSCode 同样能代码自动补全 forEach 方法以及 name 的方法来(String.pototype)。

而下面的例子表示一个自定义的对象:

/**
 * say hello 
 * @param { { name: String } } person 
 */
function sayHello(person) {
    console.log(person.name); 
}
复制代码

@param 标明函数的参数的类型;@returns 标明函数的类型。

标注自定义类型

其实在上面的例子里就有了关于自定义类型的说明(对象的那个),但上述的那个例子是针对字面量的声明,现在介绍一下 VSCode 对 JavaScript 类的处理。

class Person {
    /**
     * Person  
     * @param { String } name 
     */
    constructor(name) {
        this.name = name; 
    }

    /**
     * @returns { Person }
     */
    sayHello() {
        console.log('Hello, i am', this.name); 

        return this; 
    }

    /**
     * @param { Person } friend 
     * @returns { Person } 
     */
    playWith(friend) {
        this.sayHello(); 
        console.log('and i will play with', friend.name); 

        return this; 
    }
}

复制代码

在这里声明了 Person 类,上面有方法 sayHello 还有一个 name 属性。在 VSCode 看来,这也是一种类型,可以作为 @param 的参数使用(上例的 playWith 函数)

顺便一提,JSDoc 里的类型标注的语法主要来自于 Google Clojure Compile 里的类型表达式。

泛型(有限制的)

JSDoc 同样提供了一定能力的 泛型,但这种泛型限制很大,跟真正的泛型差的比较远。

回到刚刚的例子 add(x, y) 如果 x y 限定死了数字,那字符串类型的 x y 的相加是否还需要重新编写一次 add 呢?其实不用,可以使用 @template 的方式来完成。

/**
 * 加法
 * @template T
 * @param { T } x 
 * @param { T } y 
 * @returns { T }
 */
function add(x, y) {
    return x + y; 
}
复制代码

此处引入了 T,可以这样理解这段 JSDoc:

  1. x 和 y 的类型都是 T,说明它们的类型是一样的,返回值也一样。
  2. 若没有 @template 来声明 T,那 VSCode 会认为它是构造函数 T 的类型,此处的 @template 声明是说明 T 是“泛型”,它是一种特殊的变量,只用于表示类型而不是值

在使用了 @template 之后:

这说明,d 和 z 的类型需要通过 VSCode 理解了 add 的 JSDoc 之后才能推导出来,而 T 似乎是一个 变量,在上面两种情况下分别为 string 和 number

.d.ts

还有一种类型标注的方法即使用 *.d.ts 文件,通常普通的 JavsScript 库是不带类型标注的,这样会不兼容 TypeScript 里的类型检查,因此需要引入 d.ts 用于描述 js 库的类型让 ts 能够使用。

当然,VSCode 可以读取它,并方便开发。编写 test.js:

/**
 * @param { Person } p
 */
function test(p) {
    
}
复制代码

然后编写 Person 的类型描述 test.d.ts

declare interface Person {
    name: string, 
    age?: number, 
    sayHello: () => Person, 
    playWith: (friend: Person) => Person
}
复制代码

注:age 的问号代表年龄可有可无。

然后 VSCode 会找到 *.d.ts 文件并读取,得到类型信息:

d.ts

参考链接

http://www.css88.com/doc/jsdoc/about-block-inline-tags.html https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler#type-expressions https://code.visualstudio.com/Docs/languages/javascript#_type-checking https://zh.wikipedia.org/wiki/JavaScript


原文发布时间为:2018年06月29日

作者:eczn

本文来源:掘金 如需转载请联系原作者

相关文章
|
7月前
|
JavaScript 前端开发 编译器
TypeScript【可选属性、只读属性、额外的属性检查、函数类型、类类型、继承接口】(四)-全面详解(学习总结---从入门到深化)
TypeScript【可选属性、只读属性、额外的属性检查、函数类型、类类型、继承接口】(四)-全面详解(学习总结---从入门到深化)
75 0
|
JavaScript
TypeScript-类方法修饰符和TypeScript-类可选属性和参数属性
TypeScript-类方法修饰符和TypeScript-类可选属性和参数属性
64 0
|
JavaScript
TypeScript:类型标注和d.ts类型声明文件的使用
TypeScript:类型标注和d.ts类型声明文件的使用
191 0
TypeScript:类型标注和d.ts类型声明文件的使用
|
传感器 IDE 程序员
Python 代码智能感知 —— 类型标注与特殊的注释(所有人都需要知道)
Python 代码智能感知 —— 类型标注与特殊的注释(所有人都需要知道)
246 0
学习TypeScript27(infer 类型提取)
类型参数 T 通过extends 约束 只能是数组类型,然后通过infer 声明局部 First 变量做提取,后面的元素可以是任意类型,然后把局部变量返回
65 0
|
编译器 C++
C++模板总结, 外加一些模板的特殊用法(语法), 比如非类型模板参数, 模板的特化全特化, 以及真正理解为何模板不可以分离编译
C++模板总结, 外加一些模板的特殊用法(语法), 比如非类型模板参数, 模板的特化全特化, 以及真正理解为何模板不可以分离编译
C++模板总结, 外加一些模板的特殊用法(语法), 比如非类型模板参数, 模板的特化全特化, 以及真正理解为何模板不可以分离编译
|
Android开发
eclipse文档注释设置、文件(Files)注释标签、类型(Types)注释标签(类的注释)、字段(Fields)注释标签、构造函数(Constructor)标签
设置注释模板的入口Window->Preference->Java->CodeStyle->CodeTemplate然后展开Comments节点就是所有需设置注释的元素。本文将每一个元素逐一给大家介绍一下。方法(Methods)标签、覆盖方法(Overriding Methods)标签、代理方法(Delegate Methods)标签、getter方法标签、setter方法标签.........
157 1
eclipse文档注释设置、文件(Files)注释标签、类型(Types)注释标签(类的注释)、字段(Fields)注释标签、构造函数(Constructor)标签
|
JavaScript 前端开发 开发者
微软提议对 JavaScript 进行重大修改:将添加类型标注(Type Annotations)
微软提议对 JavaScript 进行重大修改:将添加类型标注(Type Annotations)
113 0
微软提议对 JavaScript 进行重大修改:将添加类型标注(Type Annotations)
|
Java 开发者
自定义标签之标签体内容类型|学习笔记
快速学习自定义标签之标签体内容类型。
221 0