TypeScript是由微软开发的一种开源编程语言,它是JavaScript的超集,在其基础上添加了可选的静态类型和基于类的面向对象编程。TypeScript可以编译成纯JavaScript,支持所有的JavaScript语法,因此可以在任何浏览器、任何计算机和任何操作系统上运行。TypeScript使得开发者可以使用一些未来JavaScript标准中的特性,让大型JavaScript应用可以使用更好的工具并拥有更清晰的结构。
TypeScript与ECMAScript、JavaScript的关系
ECMAScript是一个由ECMA International进行标准化,TC39委员会进行监督的语言。通常用于指代标准本身。
JavaScript是ECMAScript标准的各种实现的最常用称呼。这个术语并不局限于某个特定版本的ECMAScript规范,并且可能被用于任何不同程度的任意版本的ECMAScript的实现。
ECMAScript 5(ES5)是ECMAScript的第五版修订,于2009年完成标准化。这个规范在所有现代浏览器中都相当完全的实现了。
ECMAScript 6(ES6),又称ECMAScript 2015(ES2015)是指ECMAScript的第六版修订,于 2015 年完成标准化,这个标准被部分实现于大部分现代浏览器。
虽然JavaScript是ECMAScript规范的标准实现,但是并不是所有浏览器都支持最新的ECMAScript规范,这也就限制了开发者使用最新的JavaScript/ECMAScript特性。
TypeScript同样支持最新的ECMAScript标准,并且能将代码根据需求转换为ES3/5/6,这也意味着开发者随时可以使用最新的ECMAScript特性,比如模块、接口、类等等,而无需考虑兼容性问题。
TypeScript的安装和编译
TypeScript的安装非常的简单,使用npm安装即可:
$ npm install -g typescript
$ tsc -v
Version 2.1.6
在windows环境下,还可以安装TypeScript的Visual Studio插件。
安装好之后,使用tsc
命令就可以将TypeScript编译成JavaScript了:
$ tsc xxx.ts
$ ls
xxx.ts xxx.js
TypeScript的语言特性
TypeScript是一种给JavaScript添加特性的语言扩展,增加的功能主要有:类型注解和编译时类型检查、接口、枚举,同时从ES6中反向移植过来了:类、模块、箭头函数等功能。
类型注解(Type annotations)和编译时类型检查(Compile time type checking)
类型注解在TypeScript中是记录函数或变量约束的简便方法,基于代码结构和类型注解可以提供静态分析。TypeScript在编译时启动类型检查,但是这是可选的,而且可以被忽略而使用JavaScript常规的动态类型。
对于基本类型的注解是number, bool和string,而弱或动态类型的结构则是any类型。
类型注解可以被导出到一个单独的声明文件以让使用类型的已被编译为JavaScript的TypeScript脚本的类型信息可用,因此注解可以为一个现有的JavaScript库声明,就像已经为Node.js和jQuery所做的那样。当类型没有给出时,TypeScript编译器利用类型推断以推断类型,但是如果由于缺乏声明,没有类型可以被推断出,那么它就会默认为是动态的any类型。
function greeter(person: string) {
return "Hello, " + person;
}
var user = [0, 1, 2];
document.body.innerHTML = greeter(user);
如上代码在编译时将报错:
greeter.ts(7,26): Supplied parameters do not match any signature of call target
类(Classes)
在ES6中添加了基于类的面向对象编程语法,由于TypeScript是基于ES6的,所以开发者现在就已经可以开始在TypeScript中使用基于类的面向对象的语法了。TypeScript的编译器会将TypeScript代码编译为兼容主流浏览器和平台的JavaScript代码。
class Student {
fullName: string;
constructor(public firstName, public lastName) {
this.fullName = firstName + " " + lastName;
}
greet(name?: string) {
if (name) {
return "Hi! " + name + "! my name is " + this.fullName;
} else {
return "Hi! my name is " + this.fullName;
}
}
}
var someone = new Student("Jane", "User");
console.log(someone.greeter());
console.log(someone.greeter('Prof. Halsey'));
接口(Interfaces)
在TypeScript中,可以使用接口来确保类拥有指定的结构。接口可以看做是一个集合,这个集合是对对象、类等内部结构的约定。
interface Person {
speak(sth string): void;
}
class Student implements Person {
speak(sth) {
console.log('I am a student, and you want me to speak: ' + sth);
}
}
在上面的代码里,我们定义了一个名为Person的接口,和一个实现了它的Student类。
TypeScript也允许使用接口来约束对象,如果两个类型其内部结构兼容,那么这两种类型兼容。这样可以使我们避免很多潜在的小错误,尤其是在写对象字面量时:
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
var user = { firstName: "Jane", lastname: "User" }; //错误的属性名lastname,应为lastName
document.body.innerHTML = greeter(user);
模块(Modules)
模块在其自身的作用域里执行,而不是在全局作用域里;定义在一个模块里的变量、函数、类等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。 相反,如果想使用其它模块导出的变量、函数、类、接口等的时候,你必须要导入它们,可以使用import形式之一。
// Validation.ts
export interface StringValidator {
isAcceptable(s: string): boolean;
}
// ZipCodeValidator.ts
export const numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
//
import { ZipCodeValidator } from "./ZipCodeValidator";
let myValidator = new ZipCodeValidator();
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
let myValidator = new ZCV();
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();
箭头函数(Arrow Function)
TypeScript实现了ES6中的箭头函数,在定义函数时如果不想使用函数语法,还有另一种语法可以选择,也可以在函数的返回值类型后加上箭头(=>)操作符并不使用function关键字:
var greet = (name: string): string => {
if (name) {
return "Hi! " + name;
}
else {
return "Hi! my name is " + this.fullname;
}
};
箭头函数除了不需要function关键字,还在词意上捕获了this的意义。
function Person(age) {
this.age = age
this.growOld = function() {
this.age++;
}
}
var person = new Person(1);
setTimeout(person.growOld, 1000);
setTimeout(function() { console.log(person.age); }, 2000);
以上代码将输入1而不是2。这是因为JavaScript中的this关键字是当前运行环境中的对象,growOld方法在运行时this被设置成了window而不是Person对象。
为了解决这个问题,我们可以在函数被返回时就绑好正确的this。这样的话,无论之后怎么使用它,都会引用绑定的Person对象。我们将growOld定义的函数表达式改为使用箭头函数,箭头函数能保存函数创建时的this值,而不是调用时的值:
function Person(age) {
this.age = age
this.growOld = () => {
this.age++;
}
}
var person = new Person(1);
setTimeout(person.growOld, 1000);
setTimeout(function() { console.log(person.age); }, 2000);
结语
TypeScrit提供了可选的静态类型检查、更好的代码组织结构、更好的代码可读性,可以提前体验ES6特性,还有更为强大的IDE智能提示,而几乎不需要付出额外的代价。关于TypeScript的魅力,本文只是粗略地介绍了其中的一二,如果对此有兴趣的话,还是请移步TypeScript官网继续学习吧!