昨天偶然發現idea竟然支持typescript了,因而打算嘗試一下typescript,目前的感受還不錯,相比haxejs,它與angularjs之間的配合要流暢得多。javascript
Typescript與Coffeescript都是對javascript的改進,但二者走的是不一樣路線。Coffeescript是從語法的角度,經過提供相似於python/ruby的語法,讓代碼寫起來更加簡潔,可讀性更好。而且它提供的一些控制結構,能夠避開Javascript中的問題,好比for ... in ...
,使用coffeescript可讓多層嵌套看起來不那麼痛苦:html
self.validate json, (err, json) -> if err then cb(err) else self.mapFiles json, (err, json) -> if err then cb(err) else self.addFields json, (err, json) -> if err then cb(err) else self.store.create json, cb
回調的參數放在右邊,看起來就像是把前面函數的返回值放在了右邊供調用,讀起來比較輕鬆。習慣於python/ruby的開發者可能會比較喜歡coffeescript,我也以爲它在這方面很好。前端
而Typescript走的是另外一條路,經過增長靜態類型,提升程序的可靠性,並無從語法層面進行大的改進。html5
我以爲它們二者是互補的,若是能把二者結合起來,既提供靜態類型,又加強語法就太好了,不過這可能在好久之後纔有可能出現。java
Idea並無聲明已經支持typescript,沒有看到相關插件,在建項目時也沒有任何提示,只有在建立一個以.ts結尾的文件時,它能夠識別出來,說明這仍是一個實驗性的功能,沒有徹底完成。不過通過個人試用,基本功能都有了:node
其中編譯爲js文件的功能彷佛有點問題,另外module()函數的檢驗有時候也不太對,不過這並不影響咱們的使用。儘管仍是一個開發中的功能,但已經跟idea對haxe的支持不相上下了。python
另外,你還可使用VS2012+typescript插件,這是官方推薦的。另外喜歡用sublime text2的同窗,可到這裏下載:https://github.com/raph-amiard/sublime-typescript。另外vim/emacs也有相關的插件也有,eclipse這裏彷佛尚未動靜。jquery
聽說typescript提供了一些服務性質的api,可讓IDE實現功能(如代碼提示等)更加容易。c++
官方地址:http://www.typescriptlang.orggit
先到nodejs網站下載並安裝nodejs,而後運行如下命令:
npm install -g typescript
其中npm是由nodejs提供的包管理工具,安裝nodejs後就直接可用了。
Typescript相關的資料很少,目前官網上僅有一些示例和簡單的文檔,我收集的有如下這些連接
Typescript官方的文檔很簡單,只給出了一個簡單的例子,沒有詳細的文檔來說解各功能,因此對於初學者入門仍是稍有難度,我花了很多時間才基本會用,比我預想的時間多了不少。
難度不是在語言特性上,而是在代碼的組織和示例上,實在太少。好比如何調用聲明文件,如何按module來組織代碼等,對於初次接觸typescript的人還須要一些時間理解。
我對Typescript的印象,可總結爲如下幾點:
Typescript中提供了class、interface關鍵字,可將代碼以類的方式組織在一塊兒。構造函數爲constructor,靜態方法前要加static。在實例方法中調用靜態方法時,必須在前面加上類名。這塊看起來比較普通,很好理解。
class User { constructor(name:string, age:number) { } hello() { alert("hell, " + name); User.test(age); } static test(n:number) { console.log("this is a static method, argument: " + n); } }
這塊對於非Javascript程序員來講,有點不太好理解。我以前雖然學習了一段時間的nodejs,但如今已經快忘光了,這幾天拾起來的時候,仍是很痛苦的,資料不多。如今只能算是有所瞭解,講的可能不全對。
Javascript在語言層面沒有提供模塊機制,當代碼多了之後,很難組織起來。好比在服務器端,將不一樣的功能分在不一樣的文件中以複用,或者在瀏覽器端,想把一個大文件分紅多個,按需下載。但文件之間依賴關係、如何向外暴露對象供使用等,都沒有規定。因此人們作了不少嘗試,制定了一些規範來解決這個問題,其中比較有名的有兩個名詞,一個commonjs,一個是amd。
關於它們的介紹和相互關係,能夠看這篇文章,說得很好:http://www.udpwork.com/item/3978.html
簡單地說,commonjs是一套規範,它定義瞭如何將代碼組織爲模塊,如何向外暴露對象,若是依賴。在導入模塊時,又能夠分爲同步和異步,通常可認爲commonjs表明同步,AMD表明異步。服務器端代碼更須要同步,瀏覽器那邊更須要異步,在typescript的編譯器中同時支持這兩種方式。但commonjs是默認的,因此我前面說感受它更偏向後端。
當咱們在代碼中使用了module()函數時,Typescript能夠把它們編譯爲commonjs或amd方式的調用。好比:
/// <reference path="./libs/underscore.d.ts"/> import _ = module("underscore");
當咱們這樣編譯時:
tsc test.ts
它會生成這樣的js代碼:
var _ = require("underscore");
當咱們指定爲amd時:
tsc --module amd test.ts
它會生成這樣的代碼:
define(["require", "exports", "underscore"], function(require, exports, _____) { var _ = _____; });
在服務器端咱們通常用前者,在瀏覽器端通常使用後者(或者徹底不用module)。
若是咱們在typescript使用了module函數,則生成的代碼在瀏覽器端執行時,須要有一些script loader的支持。對於瀏覽器端代碼,咱們通常生成amd風格的代碼,因此須要找一個支持amd的庫放在前端。這樣的庫有不少,好比:
可根據本身的須要使用。我嘗試了RequireJS,不喜歡它的網站風格,寫了那麼多,但老是沒說重點。好比關於它的配置,咱們須要面對的第一個問題,但是它就是沒給一個示例出來,讓我在網上處處找別人寫的例子。要想用好它,可能得先好好讀它的文檔,再到網上找別人的代碼看。
因此最後我仍是按照傳統的方式來組織代碼,在typescript中徹底不使用module函數,而用了全局聲明的方式:
/// <reference path="../../libs/underscore.browser.d.ts"/> declare var _:UnderscoreStatic;
而不是
/// <reference path="../../libs/underscore.browser.d.ts"/> import _ = module("underscore")
當使用declare var
來聲明某變量時,即假設它已經在全局中可用,後面的UnderscoreStatic
則是在underscore.browser.d.ts
這個文件中定義的,關於underscore提供的全部方法的接口描述。
使用這種方式時,咱們保證這段js在瀏覽器端運行時,已經導入了underscore.js。咱們能夠按照傳統的方式來引用js文件:
<script src="http://freewind.me/blog/20130128/.../jquery.js"></script> <script src="http://freewind.me/blog/20130128/.../underscore.js"></script> <script src="http://freewind.me/blog/20130128/.../myapp.js"></script>
若是你既不想用requirejs等庫,又想異步下載js文件,能夠考慮這個庫:http://headjs.com/
headjs不支持commonjs/amd,它有本身的異步下載的api,十分簡潔好用。配合我上面的declare var方式,很好用。
這裏給出一個簡單的例子(headjs+jquery+underscore+angularjs+本站js):
<script src="http://freewind.me/public/javascripts/head-0.99.min.js"></script> <script type="text/javascript"> head.js('/public/libs/jquery-ui-1.8.24/js/jquery-1.8.2.min.js', '/public/libs/jquery-ui-1.8.24/js/jquery-ui-1.8.24.custom.min.js', '/public/libs/underscore/1.4.3/underscore.min.js', '/public/libs/bootstrap-2.1.1/js/bootstrap.min.js', '/public/libs/angular-1.0.2/angular.min.js', '/public/libs/angular-ui-0.3.2/angular-ui.min.js', '/public/libs/marked.js', '/public/libs/slickswitch/js/jquery.slickswitch.js', '/public/libs/html5.js', '/public/libs/flot/0.7/jquery.flot.js', '/jsRoutes.js', '/public/libs/moment/1.7.2/moment.min.js', '/public/javascripts/angular-config.js', '/public/javascripts/app.js'); head.ready(function () { app.value("AdminCommonData", { "menuTree": [], }); }); </script>
在head.js()
方法中,可傳入多個js路徑,它們並行下載,但按照聲明順序依次執行。能夠把一個head.js()分開寫成多個。head.ready()
方法將會在全部js下載完成後執行裏面的回調函數。
headjs有一個很是好用的特性,便可以把head.js()放在head.ready()的後面:
head.js(".../a.js"); head.ready(function() { console.log('I'm ready')}); head.js(".../b.js");
其中的head.ready()函數,雖然寫在"b.js"前面,但仍是會等到b.js下載並執行完後,纔會執行。
若是咱們的模板引擎使用了繼承關係,則該特性頗有用。好比在play中有一個layout.html文件和內頁main.html,咱們能夠這樣組織代碼。
layout.html
<html> <head> <script src="http://freewind.me/public/javascripts/head-0.99.min.js"></script> <script type="text/javascript"> head.js('/public/libs/jquery-ui-1.8.24/js/jquery-1.8.2.min.js' // 全部全局通用的js放在這裏); </script> </head> <body> #{doLayout /} </body> </html> <script> head.ready(function() { // 最後執行的啓動代碼放在最後 }); </script>
main.html
#{extends "layout.html" /} <script> head.js(".../main.js", // 僅在本頁中使用的js文件) </script> <div> ... </div> <script> head.ready(function() { // 能夠在這裏爲layout.html最後的函數調用準備數據 }); </script>
在這裏要補充一句,若是你使用angularjs+headjs的話,不能在<html>上聲明ng-app="xxx",而應該在layout.html中最後的函數中調用:
<script type="text/javascript"> head.ready(function () { angular.bootstrap(document, ["MyModule", "MyAnother"]); }); </script>
注意必定要把<html>上的ng-app去掉。我以前嘗試把它們兩個結合使用老是失敗,此次終於找到緣由。
若是你仔細看了前面的例子,會發現有一些以三個斜槓開頭的代碼,如:
/// <reference path="../../libs/underscore.browser.d.ts"/>
它是一種註釋,告訴typescript編譯器,當前文件使用了哪些聲明文件,以幫助編輯器提示信息,及編譯器檢查類型。這種註釋很重要,若是後面的路徑不對,則編譯會失敗。
引用的文件以.d.ts
結尾,它們是一種聲明文件,就像是c語言中的header文件,只包含了類或函數的簽名,而沒有實際內容,用於編輯器提示和編譯器驗證。它們的內容形如:
declare interface UnderscoreVoidListIterator { (element : any, index : number, list : any[]) : void; } declare interface UnderscoreMemoListIterator { (memo : any, element : any, index : number, list : any[]) : any; } declare interface UnderscoreListIterator { (element : any, index : number, list : any[]) : any; }
這種文件很重要,由於咱們要想使用第三方的js庫,通常都須要手動作出這樣的聲明文件,才能在typesafe的環境中使用它們。只須要以註釋的方式寫上便可,不須要在實際代碼中聲明或引用什麼。
如今在idea中,這些引用還得手動去寫,不太方便,我想tsc或者編輯器應該會對它進行加強。
如今有不少優秀的第三方js庫,咱們要在typescript中使用它們,難道要一一手動建立這些文件嗎?這個工做量可不小。
好在已經有人這麼作並把成果開源出來了,咱們能夠直接下載它們,放在本身的項目中,再加上引用註釋便可。
包含幾乎所有的:https://github.com/borisyankov/DefinitelyTyped,你應該把它clone到本地。
使用方法:把它們clone到本地,放在某個地方(好比工具目錄中),而後在咱們本身的typescript中添加以一個引用便可:
/// <reference path="../../libs/AngularTS.module.d.ts"/> /// <reference path="../../libs/underscore.browser.d.ts"/>
若是咱們用typescript寫了一些模塊,想讓別人調用,除了把整個代碼複製給他之外,還能夠生成一個聲明文件(.d.ts
),讓他使用該聲明文件便可。tsc提供了選項讓咱們生成.d.ts:
tsc --declaration my.ts
若是一切正常,將會在當前目錄下產生一個my.d.ts
文件,裏面包含了my.ts中定義的代碼的接口聲明。
Typescript在javascript的基礎上提供了類型系統,但它同時也支持動態類型。若是咱們把一個變量或者返回值聲明爲any
,則它表示「動態類型」,編譯器不會檢查它的類型信息,但咱們也得不到編輯器的提示信息。
function(obj: any) { obj.non_exist_method(); }
如代碼中所示,obj被聲明爲any,雖然內部調用了一個不存在的方法,編譯器也不會提示有誤,只有在運行期才知道。
雖然typescript支持靜態類型,但咱們並不須要像java那樣,在每一個地方都要聲明類型,由於typescript能夠推斷:
var name = "Freewind";
則name會被認爲是string類型.
function myname(name:string) { return name; }
則myname函數的返回值被認爲是string類型。
但咱們在聲明的地方,最好仍是加上類型,之後看起來會比較清楚。
any | 可表示動態類型 |
string | 字符串 |
number | 數字 |
bool | true或false |
null | null |
undefined | undefined |
void | void |
string[] | 字符串數組 |
{a;b;} | 等於{a:any; b:any;} |
{ a:string, b: number; } | |
{ a:string, ()=>number; } | 後面那個是函數 |
() => void | 表示形如 function() {} 這樣的函數 |
(string) => number | 表示形如 function(name:string) { return 10; } 這樣的函數 |
{ [string]: number; } | 表示一個object,它的key爲string,值爲數學,形如: { "aaa": 111, "bbb": 222} |
this.filter((todo: Todo) => todo.get('done'));
若是你使用的編輯器還不支持自動編譯typescript,可使用grunt來編譯,還能夠進行更多的任務,如對產生的js進和合並、壓縮等工做,十分方便。
具體可參看這篇文章:在Java項目中擁抱Nodejs — 使用gruntjs編譯typescript,並將生成的js合併、壓縮
僅以目前的typescript來講,雖然在類型方面作的不錯,可是語言自己還有不少能夠改進的地方。好比簡化語法,提供更好用的控制結構,對異步進行更好的支持等。若是能夠在語言層面改進javascript那些容易出錯、不方便的地方,我想會有更多人採用typescript。
從這裏的issue列表中,能夠看到呼聲較高的特性有:
其中我對2,3,6,10,11這幾項很感興趣,若是它們實現了,則typescript的吸引力會大大加強。
對於Typescript的將來,我仍是比較看好的,由於對於服務器端的編程,類型系統是很重要的,可讓咱們的代碼質量變得更高,讓不一樣版本之間的庫的兼容性也更好。
我以前使用nodejs感受很鬱悶的一點是,某一個庫升級了(如改變了api接口),則相關的庫都出錯了,而想要找出問題很難,只能經過單元測試找到問題,再查看文檔解決。而這樣的問題在java中出現的就比較少,由於有類型系統的保證,若是接口改變了,直接編譯都會出錯。
使用typescript後,讓nodejs代碼也具備的這樣的能力,對於社區的前進是頗有幫助的。並且對於java/c#程序員來講,這是頗有吸引力的。
隨着之後typescript相關編輯器、工具的成熟,能夠預見它將和coffeescript同樣,成爲javascript開發人員的標準備選,也許會有一些庫和工具直接支持typescript,那樣的話就會有更多人來使用typescript了。
若是你對typescript也感興趣,歡迎加入QQ羣:Typescript熱情交流羣(250299804)
通過幾天的試用,發現如今使用它也仍是不夠方便。主要有幾下幾點:
綜上所述,目前使用typescript進行前端開發,問題還比較多,各類小問題都會影響開發效率。因此看樣子,只能老老實實地使用javascript(或者coffeescript?),等到typescript成熟一些後再用它。