如何建立 Node.js + TypeScript 專案

Photo by Miranda Fritz on Unsplash
Photo by Miranda Fritz on Unsplash
TypeScript 是 Microsoft 在 2012 年推出的程式語言。它無法直接在瀏覽器上執行。而是要先藉由編譯器編譯成 JavaScript 檔之後,才能執行。TypeScript 有很多好用的語法,如物件導向 (Object-Oriented) 和範型 (Generic),加上要型別宣告。

TypeScript 是 Microsoft 在 2012 年推出的程式語言。它的用途和 JavaScript 一樣,但是無法直接在瀏覽器上執行。而是要先藉由編譯器編譯成 JavaScript 檔之後,才能執行。TypeScript 有很多好用的語法,如物件導向 (Object-Oriented) 和範型 (Generic),加上要型別宣告。當開發大型專案時,TypeScript 就明顯地比 JavaScript 好用,如 Angular 就採用 TypeScript 作為第一語言。

完整程式碼可以在 下載。

建立 TypeScript Node.js 專案

建立一個 Node.js 專案。

% mkdir TypeScriptExample
% cd TypeScriptExample
TypeScriptExample % npm init -y

安裝 TypeScript 模組。

TypeScriptExample % npm install --save-dev typescript

建立 TypeScript 的設定檔,它的預設檔名為 tsconfig.json。

TypeScriptExample % vi tsconfig.json

將以下的 JSON 放入 tsconfig.json。include 是指要 TypeScript 編譯器編譯的程式碼目錄。exclude 是指要 TypeScript 編譯器略過的目錄。

compilerOptions 是編譯器設定,它有很多的選項可以設定,這些可以在 Compiler Options 中查詢到。

  • module:指定要輸出的 JavaScript 要用哪一種 Module 系統。 如 ES6 是用 import/export 語法。
  • target:指定要輸出是什麼版本的 JavaScript。
  • declaration:是否編譯器要產出 .d.ts 宣告檔。
  • outDir:編譯後輸出的目的地。
  • lib:編譯器要引入的 library 列表。例如,引入 DOM,編譯器才認得 windowdocument
  • allowJs:是否要允許編譯器可以編譯 JavaScript 檔。
  • moduleResolution:指定編譯器要用什麼方式的 Module Resolution
  • strict:是否啟動所有的 type checking 選項。
  • experimentalDecorators:是否啟用支援 ES Decorators
{
  "compilerOptions": {
    "module": "esnext",
    "target": "es6",
    "declaration": true,
    "outDir": "./lib",
    "lib": [
      "esnext",
      "DOM"
    ],
    "allowJs": false,
    "moduleResolution": "node",
    "strict": true,
    "experimentalDecorators": true
  },
  "include": [
    "./src"
  ],
  "exclude": [
    "node_modules"
  ]
}

新增一個 TypeScrpt 檔叫 Person.ts。

TypeScriptExample % mkdir src
TypeScriptExample % vi src/Person.ts

Person.ts 的內容簡單如下:

export class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  public toString(): string {
    return this.name + ' is ' + this.age + ' years old.';
  }
}

將編譯器指令加入到 package.json 的 scripts 裡。

{
  "name": "TypeScriptExample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "typescript": "^3.9.6"
  }
}

然後執行指令編譯專案。

TypeScriptExample % npm run build

編譯後,編譯器將檔案輸出到 ./lib 下面。可以看到編譯後的 JavaScript 檔,和 .d.ts 宣告檔。

TypeScriptExample % ls lib
Person.d.ts Person.js

TSLint

TSLint 是用來幫我們檢查 TypeScript 語法是否符合語法規範。在程式碼寫好後,讓 TSLint 來幫我們檢查程式碼是否有符合規範是很好的事情。讓我們將 TSLint 加入到專案吧!

安裝 TSLint。

TypeScriptExample % npm install --save-dev tslint

建立 TSLint 的語法規範設定檔,預設檔名是 tslint.json。

TypeScriptExample % vi tslint.json

TSLint 設定檔的設定選項有很多,可以在 Configuring TSLint 查詢。

把下面的內容放入我們的 tslint.json。它是延伸 tslint:recommended 這個預定義好的設定檔。然後我們加上每一行尾都要分號的設定。

{
  "extends": "tslint:recommended",
  "rules": {
    "semicolon": {
      "options": [
        "always"
      ]
    }
  }
}

修改 src/Person.ts,在第11行,把行尾的分號移除。等等 TSLint 應該要檢查到這個錯誤。

export class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  public toString(): string {
    return this.name + ' is ' + this.age + ' years old.'
  }
}

將 TSLint 的指令將入到 package.json。-p 指定 tsconfig.json 給 TSLint,這樣它會知道程式碼在哪。

{
  "name": "TypeScriptExample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc",
    "lint": "tslint -p tsconfig.json"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "tslint": "^6.1.2",
    "typescript": "^3.9.6"
  }
}

執行 TSLint 指令,可以看到它有檢查出有語法不符合規範。那就是我們剛剛移除行尾的分號。

TypeScriptExample % npm run lint

> TypeScriptExample@1.0.0 lint /waynestalk/TypeScriptExample
> tslint -p tsconfig.json

/waynestalk/TypeScriptExample/src/Person.ts:11:57
ERROR: 11:57  semicolon  Missing semicolon

npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! TypeScriptExample@1.0.0 lint: `tslint -p tsconfig.json`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the TypeScriptExample@1.0.0 lint script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     .npm/_logs/2020-07-08T04_51_16_291Z-debug.log

Webpack

Webpack 是一個 JavaScript 的 Bundle 工具。如果不了解 Webpack,這篇文章有詳細的教學。

藉由 ts-loader,我們可以讓 Webpack 處理 TypeScript。讓我們來看看要如何整合 Webpack。

安裝 webpack、webpack-cli 和 ts-loader 模組

TypeScriptExample % npm install --save-dev webpack webpack-cli ts-loader

新增 webpack.config.js,內容如下。如果不了解下面的內容,請先看完上面的 Webpack 教學。這個設定檔中,最主要的地方有兩個。一個就是 extensions 要設定為 ts 副檔名。另外一個就是加上 ts-loader,讓 Webpack 可以處以 TypeScript 檔。

const path = require('path');

module.exports = {
    mode: 'production',
    entry: './src/Person.ts',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].bundle.js',
        publicPath: '/',
    },
    resolve: {
        extensions: ['.ts', '.tsx'],
        modules: ['node_modules'],
    },
    module: {
        rules: [
            {
                test: /\.ts(x?)$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'ts-loader'
                    }
                ]
            },
        ]
    },
};

最後在 package.json 中,把 npm run build 裡面的 tsc 改為 webpack。改用 Webpack 來編譯。

{
  "name": "TypeScriptExample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack",
    "lint": "tslint -p tsconfig.json"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "ts-loader": "^7.0.5",
    "tslint": "^6.1.2",
    "typescript": "^3.9.6",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "wepack-cli": "0.0.1-security"
  }
}

執行一下指令,看看結果有什麼不同。

TypeScriptExample % npm run build

> TypeScriptExample@1.0.0 build /TypeScriptExample
> webpack

Hash: 2fbb4aea58a915d46f3f
Version: webpack 4.43.0
Time: 726ms
Built at: 07/08/2020 10:07:19 PM
             Asset       Size  Chunks             Chunk Names
../lib/Person.d.ts  135 bytes          [emitted]
    main.bundle.js   1.07 KiB       0  [emitted]  main
Entrypoint main = main.bundle.js
[0] ./src/Person.ts 194 bytes {0} [built]

結語

如果你使用過 TypeScript 開發的話,你一定會對它愛不似手。雖然剛剛開始時,會因為型態宣告的繁瑣想要放棄,但是習慣了之後,你會發現型態宣告讓程式碼的可讀性大大地提升!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

You May Also Like
Photo by Berkay Gumustekin on Unsplash
Read More

TypeDoc 教學

TypeDoc 是一個 TypeScript 的 API 文件產生器。它將程式碼中的註解提取出來,並轉換成網頁 API 文件。本文章將介紹如何在專案中使用 TypeDoc,以及如何用 TypeDoc 在程式碼中撰寫 API 文件。
Read More
Photo by Harley-Davidson on Unsplash
Read More

如何建立 Node.js CLI 專案

Node.js 讓我們可以用 JavaScript 開發命令列應用程式 (CLI, Command-line interface, application) 或是服務器端應用程式 (Server side application)。本篇會介紹如何建立一個 Node.js CLI 專案。
Read More