typescript指南
前言
typescript是angularjs2推荐使用的脚本语言。它由微软2012年首次发布。
一. typescript和javascript的区别
1.从遵循的规范上的角度:
Javascript遵循ECMA5的规范,TypeScript是语法上对ECMA6的实现。
2.从功能上说:
TypeScript提供了类、模块和接口来帮助构建组件,更方便写面向对象的程序,所以被称为更好的typescript。
3.从支持上说:
所有的浏览器都支持ES5及之前的javascript,而没有浏览器支持typescript,所以需要编译。另外typescript支持所有javascript的语法。
照片显示了ES5、ES2015、ES2016以及typescript的关系。

二. typescript的安装和helloworld程序
(1)npm安装
1 | npm install -g typescript@1.8 全局安装了1.8版本的typescript编译器和tsc程序,并且添加到环境变量中。为了确认正常,打印-v命令。
|
Salin selepas log masuk
显示typescript的版本是:

(2)运行第一个ts程序。
第一种方式。
首先在1.hello.ts程序中输入:
1 | console.log( "hello world!" )
|
Salin selepas log masuk
然后执行命令:
最后运行node来执行js文件。
第二种方式。
先装好typescript和ts-node.
1 | $ npm install typescript@1.8 -g
|
Salin selepas log masuk
再运行命令执行文件。
三. typescript从ES2015和ES2016引入的语法和特性
ES2015和ES2016有比较大的变化。
(1)ES2015的箭头函数
由来:javascript有一级函数的特性,也就是说,函数能够当做参数进行传参,很好的用法不过太啰嗦,所以ES2015提供了箭头函数,更加简洁。
ts例子1:
1 | var even=[3,1,56,7].filter(el=>!(el%2));
|
Salin selepas log masuk
通过运行ts-node命令,屏幕显示结果为:

ts例子2:
1 2 | var result=[1,2,3]
.reduce((total,current)=>total+current,0);
|
Salin selepas log masuk
通过运行ts-node命令,屏幕显示结果为:

ts例子3:
1 2 3 4 5 6 7 8 | var data=[
{price:10,total:70},
{price:94,total:340},
{price:14,total:34}
]; var sorted=data.sort((a,b)=>{
} return a.total-b.total;
})
console.log(sorted);
|
Salin selepas log masuk
通过运行ts-node命令(编译),屏幕显示正确的排序结果为:

ts例子4:
1 2 3 4 5 | function MyComponent(){this.age=42;
console.log(this);
setTimeout(()=>{
console.log(this.age);
},100);
|
Salin selepas log masuk
通过编译,屏幕显示:

(2)ES2015和ES2016中的类
首先说明一下,ES6就是ES2015。ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。
然后ECMAScript 2016就是ES7。
ES6的类依然是使用构造函数和基于原型的继承,但是语法更加方便和简洁。
下面是ES2016定义类的语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Human{
static totalPeople=0;
_name;
Human.totalPeople+=1;
}
get name(){ return this._name;
}
set name(val){this._name=val;
}
}
}
class Developer extends Human{
_languages;
super(name);this._languages=languages;
}
get languages(){ return this._languages;
}
talk(){ return super.talk()+ " And I Know " +this.languages.join( '.' );
}
} var human= new Human( "foobar" ); var dev= new Developer( "bar" ,[ "javascript" ]);
console.log(dev.talk());
|
Salin selepas log masuk
喜欢!除了类的定义体是对象外,属性和方法的书写和java很像,我喜欢的方式。
通过编译,屏幕显示结果为:

(3)定义块级作用域中可见的变量
java和c++是块级作用域。
只列举代码,表现为2点,第一个是特定代码块的变量只能代码块内部可见,第二,嵌套在内部的代码块中也可见。
javascript是函数作用域。
1 2 3 4 5 6 | var fns=[]; for ( var i=0;i<5;i+=1){
fns.push( function (){
console.log(i);
})
}
fns.forEach(fn=>fn());
|
Salin selepas log masuk
打印结果,很奇怪。

ES6代码:
1 2 3 4 5 6 | var fns=[]; for (let i=0;i<5;i+=1){
fns.push( function (){
console.log(i);
})
}
fns.forEach(fn=>fn());
|
Salin selepas log masuk
打印结果:

(4)使用ES2016装饰器进行元编程。
装饰器是ES2016的一个提案,它的依据是“在设计阶段可以对类和属性进行注释和修改”。在angular2很常用,可用来定义组件、指令以及管道,并且还能配合依赖注入机制来使用。
首先,装饰器能干吗。
装饰器典型的用法是把方法和属性标记为过期,另一个应用场景是声明式语法,从而实现面向切面的编程。其实呢,装饰器只是一个语法糖而已。装饰器目前并没有得到真正的使用。
1 | Experimental support for decorators is a feature that is subject to change in a future release.
|
Salin selepas log masuk
1 2 3 4 5 6 7 8 | get kidCount(){ return 42;
}
}
descriptor.enumerable=false;
} var person= new Person(); for (let prop in person){
console.log(prop);
}
|
Salin selepas log masuk
对应的ES5语法类似于:
1 | descriptor=nonenumerable(Person.prototype, 'kidCount' ,descriptor);
|
Salin selepas log masuk
接下来,介绍angular 2装饰器的用法。
1 | "app" "./app.html" "/" ,component:Home,name: 'home' "/" ,component:About,name: 'about'
|
Salin selepas log masuk
如果装饰器需要接收参数,那么就定义为接收参数的函数,然后由函数返回真正的装饰器。
(5)使用ES2015编写模块化的代码
angular 1.x引入了一套模块系统,不过并不支持懒加载特性。angular 2种充分利用了ES2015提供的模块系统。ES2015提供了声明式API,以及使用模块加载器的命令式API。
语法分为export和import两个方面。
第一,看一个简单的DEMO:
math.ts:
1 2 3 4 5 | export function square(x){ return Math.pow(x,2);
}
export function log(x){ return Math.log(x);
}
export const PI=Math.PI;
|
Salin selepas log masuk
math2.ts,更简洁的写法而已:
1 2 3 4 5 | function square(x){ return Math.pow(x,2);
} function log(x){ return Math.log(x);
}
const PI=Math.PI;
export {square,log,PI}
|
Salin selepas log masuk
app.ts调用,要编译的是app.ts哦:
1 2 3 | import {square,log} from "./math" ;
console.log(square(2));
console.log(log(100));
|
Salin selepas log masuk
屏幕显示效果:

第二,ES2015模块化语法带有隐式的异步行为。
比如说,
A模块依赖于B、C模块。当用户请求A模块,JS模块加载器会先加载B和C模块,才能调用A模块。这里B和C模块都是异步加载的。
第三,典型的应用场景会给导出的内容起一个名字。
使用别名导入整个模块的DEMO:
1 2 | import * as math from "./math" ;
console.log(math.log(100));
|
Salin selepas log masuk
第四,默认导出。
模块导出使用了export default语法,是一种带名字的导出。
基本的默认导出DEMO:
math3.ts:
1 2 3 | export default function cube(x){ return Math.pow(x,3);
export function square(x){ return Math.pow(x,2);
}
|
Salin selepas log masuk
Salin selepas log masuk
app3.ts:
1 | import cube from "./math3" ;
|
Salin selepas log masuk
显示结果正常:

默认导出混合其他导出的DEMO:
math3.ts:
1 2 3 | export default function cube(x){ return Math.pow(x,3);
export function square(x){ return Math.pow(x,2);
}
|
Salin selepas log masuk
Salin selepas log masuk
app4.ts:
1 2 3 | import cube,{square} from "./math3" ;
console.log(square(2));
console.log(cube(3));
|
Salin selepas log masuk
显示结果OK:

(6)ES2015的模块加载器
通过编程的方式加载app模块执行main函数,使用System对象的import方法就好了。现在代码因为缺乏配置项,所以还运行不起来。
app.ts:
1 2 3 | export function main(){
console.log(2);
}
|
Salin selepas log masuk
init.js
1 2 3 4 5 6 7 | System.import( "./app" )
.then(app=>{
app.main();
})
. catch (error=>{
console.log( "致命的错误" );
});
|
Salin selepas log masuk
四: 发挥静态类型的优势
有了静态类型,那么IDE开发环境除了避免输入错误的语法高亮,还提供精确静态分析的建议。很棒。
typescript的所有类型包含几类:
● 原生类型
● 组合类型
● Object类型
● 泛型
● any类型
(1)使用显式类型定义

除了webstorm报类型(type)错误,运行编译命令,typescript 也报错 Type 'string' is not assignable to type 'number' 。那么就是说,一旦foo设置了类型,就不能赋值为其他类型了。
(2)any类型
any类型是所有其他类型的父类,代表可以拥有任何类型的值,类似于动态类型,一方面不会报错,另一方面则放弃了typescript的优点了。
1 2 3 4 5 | let foo:any;
foo={};
foo= "bar" ;
foo+=24;
console.log(foo);
|
Salin selepas log masuk
(3)原生类型
就是javascript比较熟悉的Number、String、Boolean、Null、以及Undefined。而Enum是用户自定义的原生类型,它是Number的子类。含义是枚举用户自定义的类型,由一系列有名称的值也就是元素构成。
定义enum如下:
1 2 3 4 5 | enum STATES{
CONNECTING,
WAITING,
CONNECTED
}
|
Salin selepas log masuk
(4)Object类型
首先,讲更加通用的Array类型,它是Object的子类。
Typescript的数组,要求元素类型相同。都可以使用js的各种数组方法,比如push、join、splice等,也可以使用方括号运算符对数组元素进行访问。
数值型数组DEMO:
1 2 3 4 | let primes:number[]=[];
primes.push(2);
primes.push(3);
console.log(primes);
|
Salin selepas log masuk
any型数组:
1 2 3 4 5 | let randomItems:any[]=[];
randomItems.push(1);
randomItems.push( "foo" );
randomItems.push( "{}" );
console.log(randomItems);
|
Salin selepas log masuk
屏幕结果为

第二,说Function类型,也是Object的子类哦。
javascript有两种方式创建新函数:
Typescript增加的是参数和返回值的类型。
函数表达式:
1 | let isPrime:(n:number)=>boolean=n=>{
|
Salin selepas log masuk
函数声明:
1 | function isPrime(n:number):boolean{
|
Salin selepas log masuk
对象字面量的定义写法:
1 2 3 4 5 | let person={
_name:null,
setName(name:string):void{
}
}
|
Salin selepas log masuk
(5)定义类
typescript定义类,属性的声明式强类型的。
class-basic.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Human {
static totalPeople=0;
_name:string;
Human.totalPeople+=1;
};
get name(){ return this.name;
}
set name(val){this._name=val;
}
talk(){ return "HI,I'am" +this._name;
}
}
let human= new Human( "foo" );
console.log(human._name);
|
Salin selepas log masuk
打印结果为

成功!
(6)访问修饰符
有3个。更好的实现封装和更优雅的接口。
●public。public的属性和方法在任何地方可以访问。
●private。private的属性和方法只能在类定义内部进行访问。
●protected。protected的属性和方法可以类定义内部访问,也可以从子类访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static totalPeople=0;
constructor( protected name:string, private age:number){
}
talk(){ return "Hi,I'm" +this.name;
}
}
class Developer extends Human{
constructor(name:string, private languages:string[],age:number){
}
talk(){ return super.talk()+ " And I Know " +this.languages.join( '.' );
}
}
|
Salin selepas log masuk
(6)接口
接口定义了一组对象共同的属性和方法,称作接口签名。要实现接口,那么实现定义接口规定的所有属性和方法。
关键字就2个,interface和implements。
1 2 3 4 5 6 7 8 9 10 11 | interface Accountable1{
goIncome():number;
accountNumber:string
}
class Value implements Accountable1{
constructor( public accountNumber:string){this.accountNumber=accountNumber;
}
goIncome():number{ return 100;
}
} var extra= new Value( "余额" );
console.log(extra.accountNumber);
|
Salin selepas log masuk
打印为

接口实现,成功!
接口继承
接口之间可以互相继承。接口继承另外一个接口,使用extends关键字。
interface-extends.ts:
1 2 3 4 5 6 7 | interface Accountable2{
accountNumber:string;
getIncome():number;
}
interface Individual extends Accountable2{
ssn:string;
}
|
Salin selepas log masuk
实现多个接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | interface People{
age:number;
name:string;
}
interface Accountable{
accountNumber:string;
goIncome():number
}
class Person implements People,Accountable{
name:string;
accountNumber:string;
constructor(age:number,name:string,accountNumber:string){
}
goIncome():number{ return 10;
}
} var person= new Person(10, "100" , "1000" );
|
Salin selepas log masuk
五: 使用类型参数编写泛型代码
可以写类似java的泛型代码,好精彩!使用“”。能写出更简洁的代码来。
(1)使用泛型类
定义一组类型的class。
1 2 3 4 5 6 7 8 9 10 | class Nodes<T> {
value:T;
left:Nodes<T>;
right:Nodes<T>;
}
let numberNode= new Nodes<number>();
let stringNode= new Nodes<string>();
numberNode.right= new Nodes<number>();
|
Salin selepas log masuk
(2)使用泛型函数
定义一组类型的函数。
1 2 3 4 5 | function identify<T>(arg:T){ return arg;
}
interface Comparable{
compare(a:Comparable):number;
} function sort<I extends Comparable>(arr:Comparable[]){
|
Salin selepas log masuk
(3)多重泛型
1 2 3 4 5 6 7 | class Pair<K,V>{
key:K;
value:V;
}
let pair= new Pair<string,number>()
pair.key= "foo" ;
pair.value=42;
|
Salin selepas log masuk
好精彩!
五: 使用typescript的类型推断机制简化代码
typescript可以猜测代码中的静态类型。好智能哦,所以可以用来省略一些代码。
1 2 3 4 | let answer=42;
answer= "42" ;
answer=42;
answer= "42" ;
|
Salin selepas log masuk
(1)最常见的类型
(2)与上下文有关的类型推断

这里可以看到e并没有规定类型,可是typescript就根据上下文推断它为MouseEvent鼠标事件。
六: 使用外部类型定义
尽管静态类型很酷,但是我们使用的大部分前端类库都是基于javascript构建的,都是动态类型。而typescript提供了额外的类型定义,来给编译器提供提示。
(1)使用预定义的外部类型定义
step1:安装typings工具
于是就安装好了typings目录,tsconfig.json、typings.json中,内容为:
1 2 3 4 | }, "exclude" : [ "node_modules"
]
}
|
Salin selepas log masuk
step2:创建基础配置
step3:搜索
step4:安装is-builtin-module
1 | $ typings install is-builtin-module --save
|
Salin selepas log masuk
这个时候,typings.json的内容变为
1 2 3 | { "dependencies" : { "is-builtin-module" : "registry:npm/is-builtin-module#1.0.0+20161031191623"
}
}
|
Salin selepas log masuk
typings目录变为

(2)自定义外部类型
step1:定义好类库的接口。
define-external-type.ts:
1 2 3 4 5 | interface LibraryInterface{
selectElements(selector:string):HTMLElement[];
hide(element:HTMLElement):void;
show(element:HTMLElement):void;
}
|
Salin selepas log masuk
step2:定义ts.d文件。
1 2 3 4 5 6 | interface DOMLibraryInterface{
selectElements(selector:string):HTMLElement[];
hide(element:HTMLElement):void;
show(element:HTMLElement):void;
}
declare var DOM:DOMLibraryInterface;
|
Salin selepas log masuk
step3:DOM通过reference引入,编译器就会找到对应的外部类型定义了。
1 2 3 4 5 6 7 | selectElements: function (selector:string):HTMLElement[]{ return [];
},
hide: function (element:HTMLElement):void {
element.hidden=true;
}
};
|
Salin selepas log masuk
这个时候,会报错。直到完全实现定义的接口为止,如下:
1 2 3 4 5 6 7 8 9 10 | selectElements: function (selector:string):HTMLElement[]{ return [];
},
hide: function (element:HTMLElement):void {
element.hidden=true;
},
show: function (element:HTMLElement):void{
element.hidden=false;
}
};
|
Salin selepas log masuk
这样分离出来,就可以把全部的外部类型定义放在同一个文件里,方便管理了。
Atas ialah kandungan terperinci typescript指南详解. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!