一、關於TS
TS => TypeScript:2012/10由微軟開發,是一個開源的、跨平臺且帶有類型系統的JS(ES6/7)超集,它可以編譯爲純JS,然後運行在任意的瀏覽器和其他環境。
TS是爲大型應用之開發而設計,它添加了可選的靜態類型、類和模塊,讓大型JS應用可以使用更好的工具並擁有更清晰的結構,目前最新版本:3.1。
類似的語言flow,facebook出品,react/vue2源碼在使用, vue3小尤也規劃用TS重構。
vue2部分源碼,摘自:vuesrccoreobserverwatcher.js:
/* @flow */
export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
...
deps: Array<Dep>;
newDeps: Array<Dep>;
depIds: SimpleSet;
newDepIds: SimpleSet;
getter: Function;
value: any;
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
)
...
1、TS的優勢
JS的痛點:
弱類型
沒有面向對象的接口規範
沒有命名空間
沒有跨文件邏輯預編譯能力
TS的收益:
a.) 更多的規則和類型限制 => 讓代碼預測性更高,可控性更高,易於維護和調試。
b.) 對模塊、命名空間和麪向對象的支持 => 更容易組織代碼開發大型複雜程序。
c.) 更多的語法糖:類,接口,枚舉,泛型,方法重載 => 用簡潔的語法豐富了JavaScript的使用。
d.) TS強大的靜態編譯/IDE支持 => 類型檢測、語法提示,可以捕獲運行之前的錯誤。
2、安裝與運行
- 全局安裝
$ npm i -g typescript
$ tsc -v
- 運行編譯
$ tsc test.ts // 逐個編譯
$ tsc *.tsx // 批量編譯, tsx是jsx類型的文件
$ tsc test.ts --watch // 實時監控,自動編譯
二、基礎知識
1、類型聲明
基本類型
let name: string = 'tom'
let age: number = 8
let success: boolean = true
// 聯合類型
let x: number | string
x = 1
x = 'ok'
數組
let arr: number[] = [1, 2]
let arr: Array<number> = [1, 2] // 範型寫法
// 只讀數組,所有可變方法都被移除了
let arr: ReadonlyArray<number> = [1, 2]
arr.splice(1, 'x') // Error
// 元組 Tuple
let arr: [number, string] = [1, 'ok']
枚舉
enum類型是對JS標準數據類型的一個補充
enum Direction{
Up = 1,
Down,
Left,
Right
}
let c: Color = Direction.Left // 3
數字枚舉有自增長的特性,字符串枚舉需初始化字面量
Any
不指定類型,任意類型,類似js弱類型
let arg: any = 1
let arr: any[] = [1, 'ok', false]
Void
void類型像是與any類型相反,表示沒有任何類型
function test(arg: string): void {
// ...
}
Null/Undefined
默認情況下null和undefined是所有類型的子類型
let u: undefined = undefined
let n: null = null // 對象? 引用類型? 堆?
Never
never類型表示的是那些永不存在的值的類型,常用定義報錯回調和無限循環的返回值
never類型也是任何類型的子類型,也可以賦值給任何類型
function fail(msg: string): never {
throw new Error(msg);
}
泛型
簡單的講就是用戶傳一個類型的參數,期望得到相同類型的返回值
function identity<T>(arg: T): T {
return arg
}
let idx1: number = identity(11)
let idx2: string = identity(11) // Error
交叉類型
交叉類型,就是將多個類型合併爲一個新的類型,類似於繼承
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{}
for (let key in first) {
(<any>result)[key] = (<any>first)[key]
}
for (let key in second) {
if (!result.hasOwnProperty(key)) {
(<any>result)[key] = (<any>second)[key]
}
}
return result
}
2、Interface(接口)
TS的核心原則之一是對值所具有的結構進行類型檢查,與外界規範化對象參數
interface Animal {
name: string
age?: number // 可選屬性
readonly sex: string // 只讀屬性
}
function test(arg: Animal) {
arg.sex = 'female' // Error,不能賦值
}
test({name: 'tom', sex: 'male'}})
接口繼承
可以繼承多個接口
interface Cat extends Animal1, Animal2 {
friend: string
}
函數類型
接口中定義了類似一個只有形數列表和返回值類型的函數
interface Cat {
(name: string, age: number): void
}
let cat: Cat = function(n: string, a: number) {
console.log(...arguments)
}
類類型
顯式地強制一個類滿足一個特定的契約。關鍵字implements
interface ClockInterface {
current: Date
setTime(d: Date)
}
class Clock implements ClockInterface {
current: Date
setTime(d: Date) {
this.current = d
}
constructor(h: number, m: number) {}
}
接口描述了類的公共的部分,而不是公共和私有兩部分。這會阻止你使用它們來檢查一個類的實例的私有部分也有特定的類型。
3、類
- 修飾符
ts增加了四種修飾符:public、private、protected、readonly
private: 僅自己用,不能被繼承,外部不可用
protected: 派生類可繼承,外部不可用
readonly: 只讀
class Animal {
public name: string
private age: number
protected sex: string
readonly family: string
constructor(arg: any) {
this.name = arg.name
}
}
class Cat extends Animal {
constructor(arg: any) {
super(arg)
this.name = arg.name
this.age = arg.age // 派生類不能繼承私有屬性
this.sex = arg.sex // OK
this.family = 'cat' // Err,只讀
}
}
let cat = new Cat({ name: 'tom', age: 8, sex: 'male' })
console.log(cat.age) // Err,私有屬性不外放
console.log(cat.sex) // Err,保護屬性不能在類外訪問
- 抽象類
抽象類做爲派生類的基類使用,一般不會直接被實例化,抽象類中的抽象方法也需在派生類中實現
abstract class Animal {
public name: string
abstract setName(name: string): void // 必須在派生類中實現
}
class Cat extends Animal {
constructor(name: string) {
super()
}
setName(name: string) {
this.name = name
}
}
let cat = new Cat('tom')
4、模塊與命名空間
命名空間
傳統的模塊是指外部模塊(文件),“內部模塊”稱作命名空間,使用namespace關鍵字
// namespace1.js
namespace ValidSpace {
const name = 'tom'
export const sex = 'male'
export interface ValidCat {
(s: string): boolean
}
}
interface myValid extends ValidSpace.ValidCat {
name: string
}
console.log(ValidSpace.name) // Error
console.log(ValidSpace.sex)
一個命名空間可以分散到多個文件中,訪問時如同一個文件,所有內容共享。
通過三斜線指令///,告訴編譯器在編譯過程中要引入的額外的文件
/// <reference path="./namespace1.ts" />
namespace ValidSpace {
export interface Cat extends ValidCat {} // 另一個文件同名空間的接口
console.log(sex) // OK
}
別名引用
命名空間可以嵌套,當引用目錄很深時可以使用關鍵字import簡化別名,如:import q = x.y.z
namespace Shapes {
export namespace Polygons {
export class Triangle {}
export class Square {}
}
}
import polygons = Shapes.Polygons;
let sq = new polygons.Square()
import會生成與原始符號不同的引用,所以改變別名的值並不會影響原始變量的值。
5、環境聲明
當一些全局的公共的環境變量不被認識時,可以用declare操作符創建一個環境聲明。
聲明變量文件一般以x.d.ts結尾的文件。
interface CustomConsole {
log(arg : string) : void
}
declare let customConsole : CustomConsole
customConsole.log('試試') // 成功
TS默認包含一個名爲lib.d.ts的文件,聲明瞭DOM(文檔對象模型),還有BOM(瀏覽器對象模型)全局變量,無須自己聲明瞭。
三、項目應用
1、配置
- webpack配置:
webpack需要添加ts相關的loader,webpack3.x最大支持[email protected]
$ npm i typescript [email protected] -D
- 項目依賴類型庫:
如果是react項目還需要一些必要的類型庫
$ npm i @types/react @types/react-dom @types/styled-components
- eslint的ts支持:
eslint添加typescript插件安裝
$ npm i typescript-eslint-parser eslint-plugin-typescript -D
.eslint.js配置
module.exports = {
"root": true,
"parser": "typescript-eslint-parser",
"plugins": [
"typescript"
],
"parserOptions": {
"ecmaVersion": 6,
"ecmaFeatures": {
"jsx": true // 啓用JSX
}
},
...
- tsconfig生成與配置:
$ tsc --init
ts相關配置:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"allowJs": true, // 可以混合開發
"jsx": "react",
"sourceMap": true,
"strict": true,
"baseUrl": "./",
"paths": {
"@/*": ["./src/*"],
"assets/*": ["./src/assets/*"],
"components/*": ["./src/components/*"],
"config/*": ["./src/config/*"],
"libs/*": ["./src/libs/*"],
"module/*": ["./src/module/*"],
"store/*": ["./src/store/*"]
},
// "typeRoots": [],
// "types": [],
"esModuleInterop": true,
"experimentalDecorators": true
},
"exclude": [
"node_modules"
]
}