likes
comments
collection
share

javascript项目中增加类型检测

作者站长头像
站长
· 阅读数 3

不知道大家在做前端项目的时候有没有遇到过类似的需求:领导不知道从哪里接手了一个前端老项目,扔给你,接手之后,你惊奇的发现,这代码写的和💩一样,这个时候难免心里有一千只羊驼赛跑。。。

虽然是坨💩,但为了窝囊费,工作还得继续不是。

其实倒也不用灰心,往往这种情况才能体现出我们程序员的品质不是。

闲话到此为止,今天就说说如何在老前端代码中引入类型检测。这里说的老前端代码指的就是没有使用ts或者其他类型检测系统的项目,这种项目写功能的时候确实爽啊,但是一旦转移给其他人,那可真的就是“以后就不关我的事了哈”。

解决问题的核心是利用了ts结合jsdoc的技术。其实,很多刚毕业的同学,如果一开始就是用typescript进行开发,难免会有“那岂不是要将js文件改成ts文件”这种疑惑(・∀・(・∀・(・∀・*)

其实我们用ts也不一定完整的使用,只用其一部分也是可以的,比如我只想用它来辅助检测类型是否正确,并不想经过它的编译步骤。

我们来一起看看怎么做吧:

首先就是要安装ts,这很好理解,不安装就无法使用:

npm install --save-dev typescript 

或者:

yarn add typescript -D

安装完毕之后,用下面的命令生成一个tsconfig.json配置文件

npx tsc --init

javascript项目中增加类型检测

如果你细心观察的话,不难发现,其实tsconfig.json作为json文件里面竟然写着注释!!而且IDE还不报错。如果大家对这个感兴趣的话,请在评论区留言哟~

不过我把注释什么的都删掉了。。

现在我们需要让ts干活了,吃白饭可不行。

随便找个文件,就src/App.js好了,在其中写上一个函数,然后调用。注意这个函数需要jsdoc注释,这也是ts对js文件进行检测的依据。

/**
 * @param {number} a 参数1
 * @param {number} b 参数2
 */
const add = (a,b) => {
  return a+b;
}

add(1,'a');

从jsdoc得知这个add函数需要两个number类型的参数,但是在调用的时候第二个参数的类型是string。

这个时候ts就该报错了。但是没有出意外的话,你会发现,ts现在并没有什么卵用。

那么为什么会这样呢?

其实答案很简单,ts默认是不检查js的,这不就和我一样吗,能偷懒绝不多干一点。、

所以你需要明确的告诉它要去检测js文件中的类型,它其实是知道根据jsdoc注释来检查的。

所以,对tsconfig.json加强配置,如下:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "allowJs": true,
    "checkJs": true
  }
}

配置的两个属性从字面上应该很好理解:项目中允许js文件存在不? 允许!要不要检测js,答案是:要!

很好!现在所有的js都开始报错了,我勒个亲娘勒~

就问你慌不慌?

莫慌,还不是你刚才没说清楚,在ts眼中,要么不干,要么全干。

这可怎么行呢?作为一门成熟的语言,这样极端,会被讨厌的。

所以,需要告诉ts干哪些文件。继续对tsconfig.json加强配置:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "allowJs": true,
    "checkJs": true
  },
  "files": [
    "src/app.js"
  ]
}

files的意思就是明确了ts的工作范围,我们目前只打算在写了jsdoc的文件中做类型检测,所以就告诉ts只需要检测这个文件就可以了。

其实,这里可以使用通配符的,一般来说会被写成:src/**/*.js

道理也很简单,不管是啥都要灵活,灵活很重要哦!

等等,还有一个重要的问题没有解决!!!

为什么所有的jsx部分都开始报错了??

javascript项目中增加类型检测

这个问题也很简单,ts初来乍到,也不认识jsx呀!

那就让它认识认识:

先买点学习资料:

yarn add @types/react -D

然后投喂给它:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "allowJs": true,
    "checkJs": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src/app.js"
  ]
}

大功告成!现在看看你刚才的代码,是不是被类型检测了呢?

javascript项目中增加类型检测

多说一句:

一般来说,上面的配置只能搭一个简单的框架,更好的办法是引入预先的类型:

在根目录下创建文件types/global.d.ts,它的内容为:

// global.d.ts
declare module '*.png' {
  const value: string;
  const ReactComponent: React.ReactComponent;
  export {ReactComponent}
  export default value;
}

declare module '*.jpg' {
  const value: string;
  const ReactComponent: React.ReactComponent;
  export {ReactComponent}
  export default value;
}

declare module '*.jpeg' {
  const value: string;
  const ReactComponent: React.ReactComponent;
  export {ReactComponent}
  export default value;
}

declare module '*.gif' {
  const value: string;
  const ReactComponent: React.ReactComponent;
  export {ReactComponent}
  export default value;
}

declare module '*.svg' {
  import React = require('react');
  const SVG: React.VFC<React.SVGProps<SVGSVGElement>>;
  const ReactComponent: React.ReactComponent;
  export {ReactComponent}
  export default SVG;
}

declare module '*.css' {
  const content: { [className: string]: string };
  export default content;
}

declare module '*.less' {
  const content: { [className: string]: string };
  export default content;
}

declare module '*.scss' {
  const content: { [className: string]: string };
  export default content;
}

declare module '*.sass' {
  const content: { [className: string]: string };
  export default content;
}

declare module '*.json' {
  const value: any;
  export default value;
}

作用也很简单,就是对一些常用的静态资源做模块声明,不要让报错。

对应的,配置文件就变成了:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": false,
    "skipLibCheck": true,
    "allowJs": true,
    "checkJs": true,
    "jsx": "react-jsx",
    "baseUrl": ".",
    "include": [
      "types/global.d.ts"
    ],
    "files": [
      "src/App.js"
    ]
  }

你没有看错,这里又新增了一个include配置项,值是一个数组,将定义的全局声明文件放了进去。

还有一件事,正经人写项目哪有不配置别名的,如果有别名,那还得告诉ts,不然就是统统报错!

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": false,
    "skipLibCheck": true,
    "allowJs": true,
    "checkJs": true,
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@src/*": [
        "src/*"
      ],
      "@assets/*": [
        "src/assets/*"
      ],
      "@component": [
        "src/component"
      ],
      "@component/*": [
        "src/component/*"
      ],
      "@lib/*": [
        "src/lib/*"
      ]
    }
  },
  "include": [
    "types/global.d.ts"
  ],
  "files": [
    "src/App.js"
  ]
}

好了,今天就到此为止了吧,总结一下:

  1. 对于老项目,直接引入ts风险太大,使用ts+jsdoc注释的方式比较温和
  2. 完成上面目标的步骤
  3. 可以结合// @ts-ignore等实现更加灵活的类型检测
  4. 灵活(圆滑)很重要!