入 TypeScript 坑有一段时间了,由衷的看好,所以决定在自留地絮叨一番。
是什么
TypeScript (以下简称TS)是巨硬推出的JavaScript转译语言,这意味着:
- 它需要编译
- TS代码会被编译成JavaScript代码
TS的特点是提供 模块、类、接口等一系列类型概念,协助开发者在编译时就定位出一些传统JavaScript项目的运行时错误,以实现巨硬所谓的”Robust components”(值得一提的是,TS是大名鼎鼎的 Anders Hejlsberg 主导的项目。
为什么
JavaScript 一般被视为一种动态类型、弱类型的语言,它拥有巨大的灵活性。 如下所示,类型的变化并不受约束:
1 2 3 4 |
var x = 10; console.log(typeof x); // result -> number x = 'hello'; console.log(typeof x); // result -> string |
那么TS引入的类型概念,又为哪般呢?来看一个示例:
我有一个求和的函数
1 2 3 |
function sum(x, y){ return x + y; } |
但是你也可以这么用,JavaScript没有意见,虽然结果不合理:
1 |
sum(100, 'hello'); // result -> 100hello |
于是,我们用TS这样的限定,保证以上调用是通不过编译的(输入都是number,返回也必须是number:
1 2 3 |
function sum(x:number, y:number):number { return x + y; } |
因为我们坚信,对一个数字和一个字符串进行求和,不是一种理智的需求——而修改过后,sum求和的结果是可信的。
类示例
在 Node 中,因为流和事件机制的强大,很多时候我们都会让自己的类去继承 events.EventEmitter,JS代码看起来就像是这样:
1 2 3 4 5 6 7 8 |
var events = require('events'); var util = require('util'); function MyClass(){ events.EventEmitter.call(this); } util.inherits(MyClass, events.EventEmitter); |
如果你熟悉Node的一套理论,倒也还好,但其实用TS,能写出更优雅的代码:
1 2 3 4 5 6 7 8 |
import events = require('events'); import util = require('util'); export class MyClass extends events.EventEmitter{ constructor(){ super(); } } |
具体的语法规范这里不做解释,有兴趣的可以参考官方的 Specification。
这里想说的是,TS生成的JS代码质量很高,上述片断生成的代码(闭包防污染也考虑到了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var __extends = this.__extends || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } __.prototype = b.prototype; d.prototype = new __(); }; var events = require('events'); var MyClass = (function (_super) { __extends(MyClass, _super); function MyClass() { _super.call(this); } return MyClass; })(events.EventEmitter); exports.MyClass = MyClass; |
在WebStorm中使用
鄙人已退 Windows 开发保平安,也就只能说说WebStorm的使用问题了:
首先,你得有一个TS编译器 🙂
1 |
npm install -g typescript |
然后,在第一次新建.ts文件的时候,WebStorm会询问是否为.ts文件添加watcher,这样在文件有保存动作时,watcher会自动调用编译器去编译&生成——我的选择是“否”:稍微复杂一点的项目都会使用Grunt/Gulp之类的自动化构建工具,编译也就会纳入为构建的一份子,那么以Grunt为例,编译这事交给 grunt-ts 就再合理不过了。grunt-ts配置示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ts: { default: { src: ['**/*.ts', '!node_modules/**/*.ts'], target: 'es5', outDir: '/repo/releases', options: { fast: "never", module: "commonjs", sourceMap: false, suppressImplicitAnyIndexErrors: true, preserveConstEnums: true } } } |
TS 在对待import require的库时,有它自己的一套理论:它需要一个.d.ts文件来说明引用库的Schema,比如引用underscore:
1 |
import underscore = require('underscore'); |
那么在项目中,需要存在一个 underscore.d.ts,其中声明这么一句(完整示例请参考underscore.d.ts:
1 2 3 |
declare module "underscore" { export = _; //... } |
然后在引用的地方声明一个.d.ts,引入:
1 |
///<reference path="underscore.d.ts"/> |
如此一来,方可正确通过编译,亦能得到WebStorm的智能提示。当然,如果不爽这种麻烦的引用,也可以不用import,直接使用如下形式来跳过TS检查:
1 |
var underscore = require('underscore'); |
一般而言,知名的第三方库,都能在 DefinitelyTyped 项目中找到 .d.ts,而WebStorm亦提供了快捷引用方式:
Preferences -> Languages & Frameworks -> Libraries -> Downloads… -> TypeScript community stubs,选你所需 Download & Install:
参考
- TypeScript Handbook:online version