其实ESLint + TypeScript的组合还是挺香的,代码风格检查 + 类型检查,能省下不少时间。

但是还是存在一些问题。比如,有时候为了减小打包大小,我们可能会选择把一些不太关键的依赖放到CDN上,然后再通过<script>来异步加载,这种脚本一般都会采用注入变量的方式来进行加载,这个时候就很麻烦。比如,我通过<script>加载了d3,但是在使用的时候,就会报错:

ESLint: 'd3' is not defined.(no-undef)
TS2686: 'd3' refers to a UMD global, but the current file is a module. Consider adding an import instead.

解决起来也还比较简单。首先处理一下ts,给ts加一个类型声明文件来扩展全局变量。之前我曾经写过相关的文章,在这里重复一下。为了方便(并不代表应该写成any,只是为了方便),在这里就暂时用一下any

// shims-global.d.ts
export {};

declare global {
  interface Window {
    d3: any;
  }
  const d3: any;
}

为什么第一行要写个export?因为ts的模块机制,需要通过这个export让编译器意识到这是一个模块。在之前的文章里也有相应的解释。

然后就是配置ESLint。网上目前主要是两种写法:

  1. 直接在rules里off掉这条规则。我觉得为了一个变量而关掉这项检查,不是一个很好的方案。
  2. .eslintrc.js里加这么一段:
module.exports = {
  globals: {
    'echarts': true
  },
};

可以是可以,但是总觉得有点奇怪。为什么后面是个true呢?

所以我去查了文档,文档上提供了好几种写法,在这里选两个常用的:

  1. 通过注释声明,只在当前文件生效。
    最简单的写法如下:
/* global var1, var2 */

还可以对全局变量的读写权限进行更细粒度的控制。写法如下:

/* global var1:writable, var2:writable */
  1. .eslintrc.js进行配置,对整个项目都生效。
module.exports = {
  globals: {
    var1: 'writable',
    var2: 'readonly'
  }
};

在这种写法里,off是有特殊含义的,可以“屏蔽”原生的全局变量。比如,我不希望组里的其他人直接使用Promise(比如Promise.resolve之类的方法,虽然我不知道什么情况下我会不想让别人用,但姑且认为我不想让其他人用),我就可以加上这么一个配置:

module.exports = {
  globals: {
    Promise: 'off'
  }
};

这样,只要试图使用Promise,就会提示:

ESLint: 'Promise' is not defined.(no-undef)

需要注意的是,这里不能写成false,否则规则不会生效,Promise仍然是可用的。也由此可见,写成true的那种写法并没有什么正确性,仅仅是因为有了一个值而已。