JavaScript 采用动态类型,在编译期不对类型进行检查,等真正执行的时候由运行时来判断类型是否出现错误,这种特性有点像汇编里的计算同样,类型很弱,不一样类型老是显式、隐式地转换。javascript
在一个普通的 Todo Demo 里类型或许没有那么重要,而当项目变得庞大起来的时候,里边有不少不一样的逻辑,当项目由另一我的接手的时候,阅读就很麻烦,由于老是要查看代码上下文的各个变量的类型(自定义类型或者原生类型),通常来讲,在前端 console.log 一下就能够看到了,但也只是一个临时的办法,治标不治本。css
固然,强类型的 JavaScript 已经很成熟了,那就是 TypeScript,但并非全部项目一开始就是 TS 写的,也有纯 JS 的项目,那么在这些项目里如何维护好类型?html
答案是在 VSCode 下使用 JSDoc;VSCode 会尽最大努力推导出类型出来的。前端
JSDoc 其实就是代码注释,在这种注释里能够标明 JavaScript 里值的类型,以及对代码的注释,一般能够在代码里看到以 @
符开头的标注,那就是 JSDoc 了。如下是一个简单的例子java
/** * 加法 * @param { Number } x * @param { Number } y * @returns { Number } */
function add(x, y) {
return x + y;
}
复制代码
上面 @param
, @returns
就说明了参数 x y 以及函数的返回值的类型。git
参考 VSCode - type-checking 能够显式的打开 JS 的类型检查:github
打开类型检查能够看到传入其余类型的参数的时候会报错,而填入了正确的类型的值以后,VSCode 就能知道 z
是什么类型的了(由于 x, y 均为数字,x + y 也理所固然的是数字)express
也就能实现代码补全提示了:数组
@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:
x
和 y
的类型都是 T
,说明它们的类型是同样的,返回值也同样。@template
来声明 T,那 VSCode 会认为它是构造函数 T 的类型,此处的 @template 声明是说明 T 是“泛型”,它是一种特殊的变量,只用于表示类型而不是值在使用了 @template 以后:
这说明,d 和 z 的类型须要经过 VSCode
理解了 add
的 JSDoc
以后才能推导出来,而 T
彷佛是一个 变量
,在上面两种状况下分别为 string
和 number
。
还有一种类型标注的方法即便用 *.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 文件并读取,获得类型信息:
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