最近開(kāi)始嘗試在項(xiàng)目中使用Typescript, 簡(jiǎn)單來(lái)說(shuō),Typescript是一個(gè)JavaScript的超集(即支持JavaScript的全部語(yǔ)法棍郎,完全兼容),TS在JavaScript的基礎(chǔ)上增加了靜態(tài)類(lèi)型檢查和面向?qū)ο蟮奶匦浴?/p>
Typescript可以在編譯階段“降級(jí)”為ES5或ES3银室,以在瀏覽器中運(yùn)行涂佃,因此Typescript獲得了和JavaScript一樣的兼容性,可以在全部的現(xiàn)代瀏覽器中運(yùn)行蜈敢。
npm是安裝TypeScript的最簡(jiǎn)潔的方式辜荠。npm install -g typescript
即可全局安裝,不過(guò)我通常會(huì)選擇安裝在項(xiàng)目的依賴(lài)中抓狭。即npm install typescript
伯病,然后通過(guò)node_modules/.bin/tsc
來(lái)運(yùn)行Typescript的編譯器。
Hello World
Typescript的文件以ts結(jié)尾否过,我們首先來(lái)編寫(xiě)一個(gè)greeter.ts的文件午笛。內(nèi)容如下:
function greeter(person){
return "Hello, "+person
}
let user = "World"
document.getElementById('app').innerHTML=greeter(user)
編寫(xiě)完ts文件之后,我們可以通過(guò)tsc greeter.ts
來(lái)將ts文件轉(zhuǎn)換為以js為后綴的JavaScript文件苗桂。這樣药磺,就可以在我們的瀏覽器中運(yùn)行了。
所以煤伟,我們?cè)倬帉?xiě)一個(gè)對(duì)應(yīng)的html文件癌佩,內(nèi)容如下:
<html>
<body>
<div id="app"></div>
</body>
<footer>
<script src="./greeter.js"></script>
</footer>
</html>
在瀏覽器中打開(kāi)這個(gè)文件木缝,就可以看到 Hello World 在頁(yè)面上了。
類(lèi)型檢查
不難看出围辙,我們希望在greeter函數(shù)中我碟,接收一個(gè)字符串來(lái)作為參數(shù)。如果我們?cè)贘avaScript中傳入一個(gè)非字符串函數(shù)姚建,會(huì)怎么樣呢矫俺?例如,修改剛剛得到的js文件桥胞,我們傳入一個(gè)空的對(duì)象:
function greeter(person) {
return "Hello, " + person;
}
var user = {};
document.getElementById('app').innerHTML = greeter(user);
我們會(huì)看到恳守,頁(yè)面上的文字變成了Hello, [object Object]
。這不是我們想要的贩虾。我們不希望其他的開(kāi)發(fā)者在調(diào)用這個(gè)函數(shù)時(shí)催烘,傳入非字符串的函數(shù)。因此缎罢,我們要檢查一下這個(gè)類(lèi)型伊群。因此,我們將ts文件修改為:
function greeter(person:string){
return "Hello, "+person
}
var user = {}
document.getElementById('app').innerHTML=greeter(user)
此時(shí)我們也試圖傳入一個(gè)空的對(duì)象策精,重新運(yùn)行tsc greeter.ts
舰始,我們會(huì)得到下面的信息:
error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'.
Typescript在編譯這個(gè)文件給出了報(bào)錯(cuò),原因是'string類(lèi)型的參數(shù)不能接受{}這個(gè)對(duì)象'咽袜,在編譯階段就將我們的想法拒之于門(mén)外丸卷。與傳入?yún)?shù)類(lèi)似,方法的返回值也可以指定類(lèi)型:
function greeter(person:string): string {
return {}
}
var user = {}
document.getElementById('app').innerHTML=greeter(user)
編譯器同樣會(huì)告訴我們:Type '{}' is not assignable to type 'string'.
值得注意的是询刹,雖然編譯器報(bào)了error谜嫉,但是依然會(huì)生成對(duì)應(yīng)的js文件。但它可能不會(huì)按預(yù)期的執(zhí)行凹联。
在較為大型的項(xiàng)目中沐兰,我們可能一不留神就將一個(gè)空的對(duì)象傳入了類(lèi)似的函數(shù),有了靜態(tài)檢查之后蔽挠,我們可以極大地避免這類(lèi)問(wèn)題住闯,讓我們的程序更加穩(wěn)定“氖纾可以說(shuō)比原,靜態(tài)檢查是一個(gè)非常必要的特性了。
接口和類(lèi)
像鴨子一樣走路并且嘎嘎叫的就叫鴨子杠巡。
-- James Whitecomb Riley
不妨設(shè)想一個(gè)問(wèn)題春寿,如果有很多不同類(lèi)型的對(duì)象,如Duck, Chicken, Fish等等忽孽,我們不能使用object來(lái)分辨他們(因?yàn)槎际莖bject)绑改,這時(shí)候就可以使用接口或者類(lèi)谢床。Typescript使用鴨式辨型法,我們可以編寫(xiě)不同類(lèi)型的接口厘线,制定不同類(lèi)型動(dòng)物的規(guī)則來(lái)分辨他們识腿,如:
interface Duck {
isDuck:boolean,
name:string
}
interface Chicken {
isChicken:boolean
}
function greeter(duck:Duck) {
return "This is a Duck, whose name is "+duck.name
}
var duck = {isDuck:true,name:"Donald"}
document.getElementById('app').innerHTML=greeter(duck)
這樣,我們就可以限定傳入greeter方法的參數(shù)造壮,必須“實(shí)現(xiàn)”了Duck接口的內(nèi)容渡讼,這里的實(shí)現(xiàn)只是形式上的實(shí)現(xiàn),換句話說(shuō)耳璧,只要有isDuck和name屬性的對(duì)象成箫,就會(huì)被認(rèn)為是一個(gè)實(shí)現(xiàn)了Duck接口的對(duì)象。就是開(kāi)頭的那句話旨枯,“像鴨子一樣走路并且嘎嘎叫的就叫鴨子”
不是所有的屬性都是必須的蹬昌,我們可以用“?”來(lái)標(biāo)記一個(gè)可選的屬性,如:
interface Duck {
isDuck:boolean,
name:string,
color?:string
}
這樣攀隔,無(wú)論有沒(méi)有color屬性皂贩,都會(huì)被認(rèn)為是一個(gè)“實(shí)現(xiàn)”了Duck接口的類(lèi)。
如果我們想要在創(chuàng)建完這個(gè)對(duì)象之后昆汹,就再也不修改它的一些屬性明刷,如isDuck,它應(yīng)該是一個(gè)只讀的屬性满粗。我們可以用readonly來(lái)標(biāo)記辈末。如下:
interface Duck {
readonly isDuck:boolean,
name:string,
color?:string
}
readonly和const的區(qū)別:readonly標(biāo)記的是屬性,而const所聲明的是一個(gè)變量映皆。
除接口之外挤聘,Typescript也支持面向?qū)ο蟮木幊獭_@是一個(gè)ES6的新特性劫扒,從ES6開(kāi)始檬洞,JavaScript也開(kāi)始支持面向?qū)ο缶幊塘恕?/p>
class Duck {
name:string
constructor(name:string){
this.name = name
}
greet():string{
return "Hello, "+this.name
}
}
var duck = new Duck("Donald");
document.getElementById("app").innerHTML = duck.greet()
上面的代碼創(chuàng)建了一個(gè)Duck類(lèi),其中包含了一個(gè)name屬性狸膏,一個(gè)構(gòu)造函數(shù)沟饥,和一個(gè)返回字符串的greet函數(shù)。我們可以通過(guò)new這一關(guān)鍵字來(lái)創(chuàng)建一個(gè)Duck類(lèi)的對(duì)象湾戳。并調(diào)用其函數(shù)贤旷。在greet函數(shù)中,我們使用this來(lái)引用類(lèi)成員砾脑。
在Typescript中幼驶,成員變量是默認(rèn)為public
的,即可以在類(lèi)的外部調(diào)用韧衣,我們可以用private
來(lái)將其限制在類(lèi)內(nèi)部訪問(wèn)盅藻,
類(lèi)似于接口中购桑,我們也可以使用readonly
來(lái)將屬性設(shè)置為只讀。 只讀屬性必須在聲明時(shí)或構(gòu)造函數(shù)里被初始化氏淑。
面向?qū)ο笾辛硪粋€(gè)很重要的特性是繼承勃蜘,我們可以使用extends
來(lái)實(shí)現(xiàn)繼承。
class Animal {
protected name:string
constructor(name:string){
this.name = name
}
greet():string{
return "Hello "+this.name
}
}
class Duck extends Animal {
greet():string{
return "Hello, this is a Duck, whose name is "+this.name
}
}
var duck = new Duck("Donald");
var animal = new Animal("Mickey")
document.getElementById("app").innerHTML = duck.greet()
document.getElementById("app").innerHTML = document.getElementById("app").innerHTML+' '+animal.greet()
這樣假残,我們的Duck就繼承了Animal中的name屬性和contructor方法缭贡,注意,我們?cè)谶@里使用protected屬性辉懒,以使得name這一屬性能夠在Duck子類(lèi)中使用阳惹。
工程配置
在項(xiàng)目的目錄下,可以使用tsconfig.json
這一文件來(lái)指定編譯時(shí)的屬性眶俩。如果我們直接在項(xiàng)目目錄下執(zhí)行tsc
莹汤,Typescript解釋器就會(huì)在當(dāng)前目錄尋找tsconfig.json
這一文件,如果當(dāng)前項(xiàng)目目錄沒(méi)有仿便,就會(huì)逐級(jí)向上尋找体啰。我們也可以使用--project
或-p
來(lái)指定項(xiàng)目目錄。
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true
},
"files": [
"ducks.ts"
]
}
上面是一個(gè)tsconfig.json
文件的示例嗽仪,全部的編譯器選項(xiàng)可以在編譯選項(xiàng)里看到荒勇。
"files"指定一個(gè)包含相對(duì)或絕對(duì)文件路徑的列表。 我們可以用"include"和"exclude"屬性指定一個(gè)文件glob匹配模式列表闻坚,如:
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
Vue/Angular的結(jié)合
很多較大型的Web框架都被Typescript所支持沽翔,Angular 2已經(jīng)整個(gè)項(xiàng)目都可以用Typescript來(lái)完成了。Vue 2.5也大大增強(qiáng)了對(duì)Typescript的支持窿凤。我們可以在快速起步里看到官方完成的各種起步模板仅偎。