当前位置 博文首页 > weixin_30906671的博客:如何开发一个基于 TypeScript 的工具库

    weixin_30906671的博客:如何开发一个基于 TypeScript 的工具库

    作者:[db:作者] 时间:2021-07-09 22:07

    为什么用 TypeScript?

    TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source. ———— TypeScript 官网

    1.第一时间发现类型错误

    据 rollbar 统计,在前端项目中 10 大错误类型如下图所示:

    其中有 7 个是类型错误(TypeError):

    • Cannot read property 'xxx' on undefined:无法在 undefined 上读取 xxx 属性,通常出现在 a.b.c 的情况。
    • 'undefined' is not an objectundefined 不是对象
    • null is not an object:null 不是对象
    • 'undefined' is not a functionundefined 不是函数,通常出现在 a.b.c() 的情况。
    • Object doesn't support property
    • Cannot read property 'length':无法读取 'length' 属性,本来期望一个数组,但是变量的实际类型却不是数组。
    • Cannot set property of undefined:无法给 undefined 设置属性。

    除了 7 个 TypeError,还有一个 ReferenceError:

    • 'xxx' is not defined:xxx 没有定义。

    还有一个 RangeError:

    • 在 JS 中,数组越界并不会抛出 RangeError,但是某些函数则抛出这个错误

    嘿嘿,看着这些错误眼不眼熟?

    由于 JavaScript 是一门很灵活的语言,所以以上这些错误往往要在代码运行时才能发现。

    2.智能提示

    在使用 JavaScript 时,编辑器的智能提示往往很有限,比如提示你最近输入的变量。

    但是基于 TypeScript,由于知道当前变量是什么类型,所以编辑器能经过类型推导后为你提示这个变量的所有属性,以及对于函数的参数进行提示和校验。

    此外,对于一般的 JavaScript 项目也可以自己编写 .d.ts 声明文件,来获取类型推导能力。

    至于其他的优点在这里就不展开了...

    综上,在项目中使用 TypeScript 能让你极大地提高工作效率,并减少大量的类型错误。

    TypeScript 初体验

    由于目前业务项目中的框架用的是 Vue.js,众所周知 2.x 版本对于 TypeScript 支持的不是很好,所以就打算先搞个工具函数库项目试试水。

    语言学习

    略,这个各种资料很多,这里就不赘述了...

    项目架构

    • 项目入口:src/index.ts,没有实质内容全是 export * from '...'(都是纯函数)
    • 实际代码:根据工具函数分类放在不同的文件中,例如
      • string(字符串相关)
      • env(环境探测)
      • url(链接地址)
      • ...
    • 单元测试:test/
    • 文档目录:docs/

    注意:package.json 中的 sideEffects 字段要写成 false,这样可以方便业务代码打包时 tree-shaking

    相关工具链

    之前对于 TypeScript 一直在观望的原因之一就是相关工具链的搭配还不是很成熟。不过现在倒是基本明晰了:

    • 1.代码转译 babel([译] TypeScript 和 Babel:美丽的结合)

    TypeScript 和 babel 都可以将你的 ES6+ 代码转成 ES5。

    但在 Babel v7 之前将两个独立的编译器(TypeScript 和 Babel)链接在一起并非易事。编译流程变为:TS > TS Compiler > JS > Babel > JS (again)

    现在只要安装 @babel/preset-typescript 这个包就能让你完美结合 babel 和 TypeScript。

    • 2.代码检查 eslint(The future of TypeScript on ESLint)

    再也不用纠结到底用 tslint 还是 eslint 了,TypeScript 官方已经钦定了 eslint(Migrate the repo to ESLint #30553)。

    • 3.单元测试 jest([RFC] Migrate Jest to TypeScript #7554)

    嘿嘿,Facebook 的 jest 和 yarn(Yarn's Future - v2 and beyond #6953) 都抛弃自家的 Flow 转投 TypeScript 的怀抱了

    • 4.代码打包 rollup(rollup 在 17 年底就使用 TypeScript 重写了)

    虽然 rollup 自己用的是 rollup-plugin-typescript,不过项目中还是选了 rollup-plugin-typescript2

    改造过程

    • 安装各种依赖
    • 配好各种配置文件(eslint、babel、commitlint、jest),其中最重要的是 tsconfig.json
    • 源代码的文件名后缀由 .js 改成 .ts(单测的文件也改)
    • 然后 TypeScript 的静态代码类型检查就会告诉你有什么错误
    • 看情况改代码,或者是加 ignore 注释(有时甚至需要改 tsconfig 的配置)

    文档

    文档标准

    TypeScript 官方有一套基于 jsdoc 的文档标准 tsdoc。

    export class Statistics {
      /**
       * Returns the average of two numbers.
       *
       * @remarks
       * This method is part of the {@link core-library#Statistics | Statistics subsystem}.
       *
       * @param x - The first input number
       * @param y - The second input number
       * @returns The arithmetic mean of `x` and `y`
       *
       * @beta
       */
      public static getAverage(x: number, y: number): number {
        return (x + y) / 2.0;
      }
    }
    复制代码

    生成文档

    于是顺藤摸瓜找到 typedoc 这个自动文档网站生成器。但这玩意儿的常规操作就是读取源代码,然后 duang 地一下,生成一堆页面。

    虽然这样解决了文档生成,但是没法进行开发时实时预览文档。

    自动生成

    0.失败尝试:结合 vuepress

    由于 typedoc 能导出 md 文件,所以尝试过将其结合 vuepress。不过由于 typedoc 在生成时会先清空目标目录下所有文件,折腾起来太麻烦(比如做个插件)。

    1.下策:手动触发文档生成

    没啥好说的,适用于有毅力的同学。

    2.中策:监听源文件变化,自动触发文档生成

    虽然能自动生成文档页面了,不过预览时没法自动刷新页面。

    3.上策:借助 webpack、gulp、grunt 自动生成文档并刷新页面(正好有这三者的 typedoc 插件)

    说到开发时自动刷新页面,第一个自然想到 browser-sync。

    • 虽说 webpack 无所不能,不过杀鸡焉用牛刀
    • grunt 这玩意儿有点儿落伍了
    • gulp 正好以前捣鼓过,加这个小需求正好

    最后这里贴一下 gulpfile.js 的代码,节省一下也有相关需求同学的时间吧。

    const gulp = require('gulp')
    const typedoc = require('gulp-typedoc')
    const browserSync = require('browser-sync').create()
    
    const runTypeDoc = () => gulp
        .src(['src'])
        .pipe(typedoc({
            out: './docs',
            // 这个文件里都是 export * from '...' 就没必要导出文档了
            exclude: 'src/index.ts',
            tsconfig: 'tsconfig.json',
        }))
    
    const reload = (done) => {
        browserSync.reload()
        done()
    }
    
    const runBrowserSync = (done) => {
        browserSync.init({
            server: {
                baseDir: './docs',
            },
        })
        done()
    }
    
    const watch = () => gulp.watch(
        ['README.md', 'src/*.ts'],
        gulp.series(runTypeDoc, reload)
    )
    
    gulp.task('default', gulp.series(runTypeDoc, runBrowserSync, watch))
    复制代码

    以上 to be continued...

    参考资料

    • TypeScript 官网
    • TypeScript 解决了什么痛点? - justjavac的回答 - 知乎
    • @babel/preset-typescript
    • [译] TypeScript 和 Babel:美丽的结合
    • The future of TypeScript on ESLint
    • Migrate the repo to ESLint #30553
    • [RFC] Migrate Jest to TypeScript #7554
    • Yarn's Future - v2 and beyond #6953
    • rollup 在 17 年底就使用 TypeScript 重写了
    • rollup-plugin-typescript2
    • tsdoc
    • typedoc
    • browser-sync
    • typedoc 插件
    • rollbar
    cs