如何開發一個基於 TypeScript 的工具庫並自動生成文檔

爲何用 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 大錯誤類型以下圖所示:html

JavaScript 錯誤類型

其中有 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:vue

  • 'xxx' is not defined:xxx 沒有定義。

還有一個 RangeError:java

  • 在 JS 中,數組越界並不會拋出 RangeError,可是某些函數則拋出這個錯誤

range error

嘿嘿,看着這些錯誤眼不眼熟?webpack

因爲 JavaScript 是一門很靈活的語言,因此以上這些錯誤每每要在代碼運行時才能發現。git

2.智能提示

在使用 JavaScript 時,編輯器的智能提示每每頗有限,好比提示你最近輸入的變量。github

可是基於 TypeScript,因爲知道當前變量是什麼類型,因此編輯器能通過類型推導後爲你提示這個變量的全部屬性,以及對於函數的參數進行提示和校驗。web

智能提示

此外,對於通常的 JavaScript 項目也能夠本身編寫 .d.ts 聲明文件,來獲取類型推導能力。

至於其餘的優勢在這裏就不展開了...typescript

綜上,在項目中使用 TypeScript 能讓你極大地提升工做效率,並減小大量的類型錯誤。json

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:美麗的結合][4])

TypeScript 和 babel 均可以將你的 ES6+ 代碼轉成 ES5。

但在 Babel v7 以前將兩個獨立的編譯器(TypeScript 和 Babel)連接在一塊兒並不是易事。編譯流程變爲:TS > TS Compiler > JS > Babel > JS (again)

如今只要安裝 @babel/preset-typescript 這個包就能讓你完美結合 babel 和 TypeScript。

不再用糾結到底用 tslint 仍是 eslint 了,TypeScript 官方已經欽定了 eslint(Migrate the repo to ESLint #30553)。

  • 3.單元測試 jest([[RFC] Migrate Jest to TypeScript #7554][7])

嘿嘿,Facebook 的 jest 和 yarn(Yarn's Future - v2 and beyond #6953) 都拋棄自家的 Flow 轉投 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...

參考資料

相關文章
相關標籤/搜索