之前讀書會分享的 TS 紀錄也同步在部落格放一份方便日後新增。
環境建立
1 | npm install -g typescript |
編譯
1 | tsc |
tsconfig:
1 | tsc --init |
1 | { |
指定 tsconfig.json 路徑
1 | tsc -p tsconfig.json |
搭配 webpack, gulp or grunt 打包
- webpack:
ts-loader
- gulp:
gulp-typescript
- grunt:
grunt-ts
安裝第三方函式庫定義檔
1 | npm install --save @types/lodash |
入門
可先到 TypeScript Playground 遊玩
1 | const x = 1; |
上面程式會輸出 12 (因為 string 優先,1 被轉換為字串了)
此時使用 TypeScript 可避免此問題
1 | const x: any = 1; |
基本型別定義:
1 | // boolean |
物件型別定義:
1 | // object type |
enum 枚舉:
可以指定變數只能是枚舉內的屬性
1 | // enum |
any:
1 | var iAmString:string = 'string is me'; |
使用 any 型別,變數就可以任意變更為其他型別的值,等同於寫原本的 js,不建議使用
function:
1 | function getPrice(price: number): number { |
2.interface / type
1 | interface IPlayer { |
兩種應用差別不大,但通常 interface
會用來定義 class 的規格,type
用來定義參數內容,視使用情況而定
3.class
1 | class Player { |
class 使用 interface1
class Player2 implements IPlayer {}
使用 implements
來實踐 interface,此時 Player2 一定要有 IPlayer 裡定義的規格否則 TS 會報錯
class extends 繼承/擴充1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class MobilePlayer extends Player implements MPlayer {
public duration: number;
public banner: HTMLImageElement | any;
constructor() {
// super 呼叫父類別的建構式,一定要放於建構式第一行
super("mobile-inread", "INSTANT");
this.duration = 30;
this.setBanner();
}
private setBanner() {
this.banner = new Image();
}
}
const m = new MobilePlayer();
m.replay()
可以看到 MobilePlayer 裡面只有一個 method
卻可以使用 replay()
而不會報錯,是因為 MobilePlayer
繼承了 Player
,所以可以使用其 method。
由於 javascript 的 class
並不是真正的以類別為基礎(class-based)的物件導向,其實是一種用 prototype
原形鏈的包裝過語法糖,只是原形鏈繼承寫法比較複雜,因此在 ES5 的寫法還是透過 prototype
來做擴充,ES6 後使用 class 比較直覺精簡,但其實骨子裡還是透過原形鏈的概念1
2
3MobilePlayer.prototype.setBanner = function () {
this.banner = new Image();
};
4.modules
內部模組運用:
namespace
可將變數儲存在共同的命名空間,防止命名衝突,透過 export
關鍵字將變數 export 出去
1 | namespace Guoshi { |
引入的方式:
1 | // 這行用來參考到用到的檔案 |
tsconfig.json 可以設定 outFile
來讓各檔案輸出成單一檔案。(並不是透過模組化,而是透過命名空間)
import / export: 外部模組的運用 (需透過模組打包工具如 webpack or browserify)
webpack 設定可參考 source code 裡的 webpack.config.js
1
2
3
4
5
6
7
8
9
10// 外部模組的運用
export const VALUE = 0.5;
export class Main {
init() {
console.log("init");
}
}
export function ajax() {
console.log("ajax call");
}
引入的方式:
1 | import { Main, ajax } from "./export"; |
5.Generic
泛型可讓型別定義更加彈性,當構建一些函式需要被共用時可以採用。
Generic function:
1 | function logAndReturn<T>(thing: T): T { |
Generic object:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 使用 <T> 可以實作不同種的 Game
interface Games<T> {
games: Array<T>;
addGame: (newGame: T) => void;
getAll: () => Array<T>;
}
type GameForChild = {
name: string;
price: number
}
let myChildGameStore: Games<GameForChild> = {
games: [],
addGame(game) {
this.games.push(game);
},
getAll() {
return this.games;
}
};
let allGames = myChildGameStore.getAll();
// myAdultGame
type GameForAdult = {
productName: string;
age: number;
type: string;
}
let myAdultGameStore: Games<GameForAdult> = {
games: [],
addGame(game) {
this.games.push(game);
},
getAll() {
return this.games;
}
};
myChildGameStore, myAdultGameStore 都是實踐 interface Games<T>
,所以裡面都必須要有 games, addGame(), getAll(), 但是因為使用了 generic ,所以這三個屬性裡的值可以彈性使用 GameForChild 或 GameForAdult (宣告時必須指定)。
同理,應用在 class
也是一樣道理。
6.window 物件與第三方函式庫
window 物件定義1
2
3
4
5// window 沒有的物件
interface Window {
ONEAD: string
}
window.ONEAD = '';
因為 TS 只會知道既定 window 底下有的物件與函式,如果有其他 script 在 window 下產生變數, TS 不會知道,必須宣告型別才不會報錯。
周圍變數宣告:1
2
3// 宣告已存在 window 裡的 第三方 lib,TS 未定義會報錯
$("#ONEAD");
jQuery("Guoshi");
使用第三方套件時,因為沒有型別定義,所以 TS 會報錯,可以透過安裝 @types
1 | npm install @types/jquery --save-dev |
或者是自己寫定義檔 .d.ts
1 | //jquery.d.ts |
與框架搭配
可參考 Microsoft 對兩大框架的 starter 包