TypeScript 是 Microsoft 在 2012 年推出的程式語言。它的用途和 JavaScript 一樣,但是無法直接在瀏覽器上執行。而是要先藉由編譯器編譯成 JavaScript 檔之後,才能執行。TypeScript 有很多好用的語法,如物件導向 (Object-Oriented) 和範型 (Generic),加上要型別宣告。當開發大型專案時,TypeScript 就明顯地比 JavaScript 好用,如 Angular 就採用 TypeScript 作為第一語言。
Table of Contents
建立 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,編譯器才認得
window
和document
。 - 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 開發的話,你一定會對它愛不似手。雖然剛剛開始時,會因為型態宣告的繁瑣想要放棄,但是習慣了之後,你會發現型態宣告讓程式碼的可讀性大大地提升!