昨天偶然发现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成熟一些后再用它。