2016-08-07

JS:如何開始在 nodejs 使用 typescript ?

如何開始在 nodejs 使用 typescript ?

結論:

安裝:
  • typescript, 當然就是 TypeScript.
  • typings, TypeScript Definition Manager. 要用到其他第三方函式庫, 就必須要型別定義檔, 自己編寫也行, 但最好還是有管理他們的工具.
> npm install -g typescript typings
> tsc -v
//Version 1.8.10
轉譯:
  • 簡單使用 tsc .
  • 或建立 TypeScript 的專案檔 ‘tsconfig.json’, 使用 tsc -p 為專案檔所在目錄.
引用:
  • 使用 typings 安裝第三方函式庫的 TypeScript definitions.
  • 修改 ‘tsconfig.json’, 將定義檔 “typings/index.d.ts” 加入 “files” 或 “filesGlob”.
> npm install  --save
> typings install  --save
// 在新版的 TypeScript 2.0 你可以用下面的命令代替 `typings install `
> npm install --save @types/
// 你可以在[這裡](https://aka.ms/types) 查有哪些可以用
// 其實應該就是 `typings search --source npm` 的 Web 版
// 但可以省掉處理 `typings` 的 `.d.ts` 檔.
監控: 使用 tsc -w -p  來監控檔案, 在檔案有變動時進行轉譯.

廢話:

ES6 改名叫 ECMAScript 2015, 好長的名字. 我還是叫他 ES6 吧. 參考一下ES6 支援度, 目前(20160805)還沒有 100% 支援的環境, 雖然他叫 ECMAScript 2015. 想要好好寫 ES6, 還是得找個轉譯器, 但取名 babel 多不吉利阿.
TypeScript 雖然明明只是個 Script, 但卻是偶像(Anders Hejlsberg)操刀. 所以我偏心了. 我連 ES7 都不知道長甚麼樣子, TS 卻去支援 ES8 的特性, 天知道是甚麼鬼阿!

正文:

使用 npm 安裝完 typescript 與 typings 後, 開始寫第一個程式: hello.ts. 沒看錯附檔名要從熟悉的 .js 改為 .ts. 就讓我們寫一個有 typescript 風格的程式碼吧, 如下
var hello = (word: string) => {
    console.log(`Hello, ${word}`);
}
hello("TypeScript");
他會轉譯為,
> tsc hello.ts; cat hello.js
/** 
    var hello = function (word) {
        console.log("Hello, " + word);
    };
    hello("TypeScript");
 */
跟一般一樣, 執行 node hello.js 即可.
但寫程式怎麼能不用第三方函式庫? 不管是 lodash 還是 async , 沒有了, 可是會直接降低一階的生產力. 雖然 ES6 新增的 Generator 和部分方法可替代, 但畢竟是在過度期, 並不是全部的環境都支援, 熟悉與法也需要一段時間. 這時候就輪到 typings 出場. 安裝完 typings 後, 會多出一個叫 “typings”(類似 NPM 中 node_modules 的角色) 的目錄來放 TypeScript 的定義檔.
> typings init
// 會產生一個 typings.json (類似 NPM 中的 package.json)檔案.
> npm install lodash --save
// 安裝 lodash, 已經安裝了就可以略過
> typings install lodash --save
// 會將定義檔放到 "typings/modules/lodash/"
改寫一下 hello.ts, 如下 helloAnybody.ts
import * as _ from "lodash";

var hello = (compiler: string) => {
    console.log(`Hello, ${compiler}`);
}

var anybody = ["TS","JS","Node"];
_(anybody).each(hello);

為了下指令方便起見, 讓我們編輯 TypeScript 的專案檔. 先利用 tsc --init 產出一個 “tsconfig.json”, 如下
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": false
    },
    "exclude": [
        "node_modules"
    ]
}
基本上 “tsconfig.json” 的檔案內容只要 {} 就可以了. 但如果是這樣所有的參數就會採內定值, 這可能不是我們要的. 其實如果是這樣有這個檔跟沒有是一樣的. 所以, 接下來就一些主要會用到的部分進行解釋與修改. “tsconfig.json” 主要有兩部分,
  • compilerOptions, 指示 tsc 應該將檔案轉譯成什麼樣的 JavaScript.
  • exclude, files, 指示 tsc 要轉譯哪些檔案.
首先是 “compilerOptions” 的部分,
  • target: 指示採用哪一版的JS. 可用的值有 “es3”, “es5”, “es6”, 內定是 “es5”. 基本上 “es5” 是一個合適的選擇, 除非你運行在一個特別舊的環境.
  • module: 指示採用哪種模組模式, 可用的值有 “none”, “commonjs”, “amd”, “system”, “umd”, “es6” 或 “es201”. 但如果 “target” 在 “es5” 以下, “es6” 或 “es201” 模式是無效的. 如果你選擇的 “target” 是 “es6”, 目前合法的模式也就只有 “es6”, 如果 “target” 不是 “es6”, 則 “module” 內定會是 “commonjs”. 因為是 node, 所以沒疑問就是 “commonjs”.
  • moduleResolution: 指示採用何種方式取得模組, 可用的值有 “node”, “classic”, 內定值是"classic". 因為我們是在 node 上開發, 所以最好是加上 {moduleResolution:"node"}. -其他就有需要時再參考文件.
再來是檔案的部分,
  • 如果都沒有指定 tsc 會轉譯所有目錄與其子目錄下的所有 TypeScript (*.ts or *.tsx) 檔案.
  • 有 “files”,  會轉譯所有吻合 “files” 的檔案. 有 “exclude”, 會轉譯所有不吻合 “exclude” 的 TypeScript 檔案.
  • 如果同時有 “exclude” 和 “files” 的條件, 則 “files” 優先.(不考慮 “exclude” 的條件)
  • “exclude” 排除的可以是目錄, 但 “files” 只能是檔案. 而且都不支援 glob.
  • “filesGlob” 支援 glob, 可以取代 “exclude” 和 “files”. 但只有 AtomTypeScript 才支援.
  • “include” 支援 glob, 但 TypeScript 要版本 2.0 以後.
新的"tsconfig.json", 如下
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "moduleResolution": "node",
        "noImplicitAny": false,
        "sourceMap": false
    },
    "files": [
        "helloAnybody.ts"
    ]
}
當你執行 tsc -p . 會出現 helloAnybody.ts(1,20): error TS2307: Cannot find module 'lodash'., 這是因為缺乏定義檔, 將之改為
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "moduleResolution": "node",
        "noImplicitAny": false,
        "sourceMap": false
    },
    "files": [
        "helloAnybody.ts",
        "typings/index.d.ts"
    ]
}
即可.
接下來安裝 async, 照例使用 typings install async --save, 卻發生錯誤. 這是因為 typings 對定義檔的 source 有區分. 大致上可以分為
  • external,
    • npm 來自 NPM, 內定使用
    • github 來自 GitHub
    • bitbucket 來自 Bitbucket
    • bower - 來自 Bower
    • common - 未知來源的基本函式庫
    • shared - 共享函式庫
  • global,
    • lib 程式環境的共享函式庫
    • env 程式環境
    • global 全域的函式庫
    • dt 來自 DefinitelyTyped
來源可以~ 或 --source 指定. 但屬於 global 類別的要再加上--global 參數. 可以在安裝前使用typings search  查詢一下.
> typings search async
/*
Viewing 9 of 9

NAME   SOURCE HOMEPAGE            SCRIPTION   VERSIONS UPDATED
async  dt     https://github.com/caolan/async 1       2016-08-04T11:33:11.000Z
......

*/
將"helloAnybody.ts"改成為非同步版本.
import * as _ from "lodash";
import * as async from "async";

var helloAsync = (compiler: string, callback: (...args: any[]) => void) => {
    console.log(`Hello, ${compiler}`);
    return callback();
}

var anybody = ["TS", "JS", "Node"];
async.each(anybody, helloAsync, (err) => { console.log('fin.') });
執行 tsc -p .後, 被轉譯如下,
> cat helloAnybody.ts
/*
"use strict";
var async = require("async");
var anybody = ["TS", "JS", "Node"];
var helloAsync = function (compiler, callback) {
    console.log("Hello, " + compiler);
    return callback();
};
async.each(anybody, helloAsync, function (err) { console.log('fin.'); });
*/
如果你使用的函式庫沒有定義檔, 而你也不在乎是否在開發環境或轉譯時檢查該函式庫的型別, 你可以更簡單的使用 --allowJs 或在tsconfig.json 加入 {compilerOptions: {allowJs : true}}. 這樣你就能告別 error TS2307.
最後, 你可以使用 -w 來使檔案變動後能自動轉譯為 JS.
> tsc -w -p .
//11:46:57 - Compilation complete. Watching for file changes.
但 -w 是一開始就會決定要監控哪些檔案, 所以就算你使用支援 glob 的 include, 後來才新增加的檔案就算符合條件也不會被監控, 要能監控新增檔案可用 gulp.watch 之類的方式來處理.

沒有留言:

張貼留言