27

JavaScript 竟然可以类型推断

 5 years ago
source link: https://mp.weixin.qq.com/s/hWxdqiXVEft_hlWSiccfMA?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

作者 | 朱士奇

杏仁前端开发工程师,代码洁癖症早期,关注前端技术。

作为弱类型的 JavaScript   写起来爽,维护起来更

—— 鲁迅·沃梅硕果

近几年,前端技术的发展可以用 Big Boom   来形容,因此   JavaScript   也被大规模的运用在项目中,由此也产生了代码的维护问题,所谓   动态类型一时爽,代码重构火葬场  

其实不仅仅是代码重构,在日常开发中也能感受到弱类型语言的不足所带来的不便之处。举个例子,现在有个函数 renderUserList   , 作用是将用户列表显示在界面上

function renderUserList(el, userList) {
let html = '';
  for (const user of userList) {
    html += `<div>    
    姓名:${user.age}    
    年龄:${user.age}    
    </div>`
  }
  el.innerHTML = html;
}

我敢打赌,大家在写这种类型函数的时候,都是在盲写,因为我们不知道传入的 el  userList   到底是什么类型。更不知道   el   下面有哪些方法,写的时候都如此费劲,跟别谈维护了。其实我们可以通过一些简单的操作,让这个函数写起来更轻松,就像下面一样:

6nIBNj6.jpg!web

V7Fv2y6.jpg!web

那么,到底是怎么实现的呢?接下来就要介绍本文的主角 JSDoc    VSCode

JSDoc是一个根据 JavaScript 文件中注释信息,生成 JavaScript 应用程序或库、模块的 API 文档 的工具。你可以使用他记录如:命名空间,类,方法,方法参数等。

通俗的讲,JSDoc 是 JavaScript 注释规范的一种,VSCode 利用 JSDoc 规范的特点,配合 typescript 实现了“类型提示”,所以在 VSCode 中基本上是 开箱即用 的,而对于非内置对象,比如 jQuery 的 $,lodash 的 _ 等,则需要单独下载对应的声明文件。

不过实际开发中,在 window 和 mac 上,还是有些差别的,mac 版的 VSCode 会去检查代码,然后自动下载对应的声明文件存放在 ~/Library/Caches/typescript/ (猜测是自动下载的),而 windows 则需要开发者手动通过 npm 去安装需要的声明文件,文末也会提到如何使用声明文件。

另外在 .jsx   中也可以使用 JSDoc,webstorm 也支持通过 JSDoc 实现类型提示, sublime 貌似还不支持。

在 VSCode 中会自动根据 JSDoc 的标注对变量、方法、方法参数等进行类型推断,通过 TypeScript 来进行智能提示,因此从编写注释开始学习 TypeScript 也是一个不错的选择,下面就来一一列举 JSDoc 在代码中的用法。

变量

@type   标注变量的类型

基础类型

/**
 * @type {number}
 */
let n;

/** @type {boolean} */
let flag;

/** @type {string} */
let str;

联合类型

如果一个变量可能是多种类型,则可以使用联合类型

/** 
 * @type {string | boolean} 
 */
let x;

自定义类型

我们经常用到自定义类型,也就是 JavaScript   中的对象,对于简单的对象,可以用下面的写法

/**
 * @type {{name: string, age: number}} 
 */
let user;

对于键值对比较多的复杂对象,可以使用 @typedef   来定义复杂类型,用   prop   或者   property   来定义对象的属性。

/**
 * @typedef {Object} goods 
 * @property {string} name 
 * @prop {number} code 
 * @prop {string=} thumbnail 用 = 表示该属性是可能存在,也可能不存在 
 * @prop {string} [introduction] 也可以给属性名加上 [] 表示这是一个可选属性 
 * @prop {string[]} label 
 */
 /**  
  * @type {goods}  
  */
 let phone;

数组

可以使用 []   或者   Array   表示数组

/**
 * @type {number[]}
 */
let numList;
/** 
 * @type {Array<string>} 
 */
let strList;

对于已经定义的类型或者已经声明的变量,也是可以直接使用,下面分别声明一个 user  数组和   goods   数组

/**
 * @type {user[]} 
 */
let userList;
/** 
 * @type {goods[]} 
 */
let goodsList;

如果不确定数组的每一项具体类型,可以使用 any   *   或者交叉类型

/**
 * @type {any[]} 
 */
let arr1;
/** 
 * @type {*[]} 
 */
let arr2;
/** 
 * @type {(user | goods)[]} 
 */
let arr3

泛型

/** * @template T * @param {T} p1 * @return {T} */
function gen(p1) { 
return p1 
}

函数

@name   表示函数的名称

@param   表示函数的参数

@return    @returns   表示函数的返回值

一般函数的写法大致分为两种:声明式函数和函数表达式。

函数表达式

/**
 * @type {function (number, number): number} 
 */
var getSum = (n1, n2) => n1 + n2;

声明式函数

/** 
 * @name fn 
 * @param {string} str 
 * @param {boolean} flag 
 * @returns {*[]} 
 */
function fn(str, flag) {
  return [];
}

通过上面的注释写法,便可以在函数 fn   内部正确的识别出两个参数的类型,并且可以知道该函数返回值类型为数组。

对于函数参数的类型,写法和上面的变量写法一致,区别是将 @type   换成了 @param ,函数的返回值也是同样的道理。

对象的方法

对函数的注释同样适用于对象的方法

var o = {
/**
 * @param {string} msg   
 * @returns {void}   
 */
  say(msg) {
    console.log(msg);
  }
}

内置类型和其它类型

上面的例子只是简单的用到了一些常见的类型,然而在实际开发中,我们用到的不止这些,比如开始文章开头的例子中,有用到了 DOM   对象,那该怎么编写注释呢?其实 VSCode   已经为我们提供了很多的类型了,比如   DOM   对象对应的类型是 HTMLElement   , 事件对象对应的类型是   Event ,同时   DOM   对象还可以更细化,比如   HTMLCanvasElement  HTMLImageElement   等等。

同时,我们在开发中也会用到第三方的类库或框架,通常情况下,这些类库都会有一份以 d.ts  结尾的声明文件,该声明文件中包含了所用到类型的所有提示,以最为经典的 jQuery   为例,如果在时在   webpack   环境下,在通过 npm 安装 jQuery 后,需要再单独安装对应的声明文件   @types/jquery   ,这样 VSCode 就可以正确的识别 $   符号,也可以在 JSDoc 中使用   JQuery ,   JQueryStatic   等这都类型了,就像下面这样

/**
 * @type {JQuery}
 */
var $btn = $('button');

/** 
 * @param {number} userId 
 * @returns {JQuery.jqXHR}  
 */
function getUser(userId) {
  return $.get(`/user/${userId}`);
}

大部分情况下,通过 npm 发布的包,都会包含其对应的声明文件,如果没有的话,可以通过这个地址 TypeSearch   来搜索一下并安装 ,如果感兴趣可以到这个仓库DefinitelyTyped   看看。当然你也可以提供一些仓库内目前还没有声明文件,别人会非常感谢你的!

当然并不是所有的项目都用到了 npm ,仍有很多项目在使用 script   这种方式从 cdn 来引入   .js   文件,这种情况下用不到 webpack ,也用不到 npm ,那这个时候就要从上面所提到的仓库地址   DefinitelyTyped   来下载对应的声明文件了,然后通过   /// <reference path="" />   这种形式来引入声明文件,就像下面这样

/// <reference path="./node_modules/@types/jquery/index.d.ts"/>

个人建议:即使是通过 cdn 方式来引入 .js   文件,也可以通过 npm 来安装 @types/   ,这样和在每个文件中通过   /// <reference path="" />   引入声明文件相比,还是方便很多的。

总结

以上便是关于利用 JSDoc 实现 JavaScript 的类型提示。当然还有一些更深入的用法,比如全局模板文件,命名空间等,但是这些和 TypeScript 关系更大一些。当有一天你发现 JSDoc 已经不能满足你的时候,便是向着 TypeScript 大举进攻的时候了。

全文完

以下文章您可能也会感兴趣:

我们正在招聘 Java 工程师,欢迎有兴趣的同学投递简历到 [email protected]

IRRbQr6.jpg!web

杏仁技术站

长按左侧二维码关注我们,这里有一群热血青年期待着与您相会。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK