TypeScript快速入门
简介
TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。
TypeScript 由微软开发的自由和开源的编程语言。
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
TypeScript增强了JavaScript,很多前端框架都开始使用TS改写,比如React,Angualr,以及Vue.js.
特性
相比于JS,增加了类型批注,泛型,接口等特点,还从ES6中移植了类、模块的语法,如果有JS/ES基础,或者面向对象语言的基础,学习起来非常容易。
安装TypeScript
安装Ts其实就是安装Ts的解释器tsc,需要用到包管理工具。
我们使用npm安装:
以下为全局安装:
npm install -g typescript
安装成功后在终端输入版本指令,得到信息则安装成功。
$ tsc -v
Version 3.6.8
TS以.ts做文件扩展名,写一个helloworld.ts测试一下。
var message:string = "Hello World"
console.log(message)
在终端使用ts解释器tsc编译为js文件。
tsc helloworld.ts
此时目录下会生成一个helloworld.js文件。
var message = "Hello World";
console.log(message);
1.0、类型批注
ts需要指定变量类型,如下所示,编译时进行检查,类似于静态语言,在编译前确定数据类型。
var message:string = "Hello World"
let str:string;//定义为string
str="Hello TS!";
函数等也需要类型批注。
function show(args:string):string{
return args;
}
2.0、数据类型
相比JS,TS增加了了部分类型,如any,never,void,enum以及数组的变化元组。
//数组,也可以使用泛型,这里的number和下文的Number分别是TS、JS的数字类型。
var arr:number[]=[1,2,3,3,5];
//元组也是数组,但允许装载不同的元素
var tur:[Number,string,boolean]=[19,"RIght",true];
console.log(...tur);//19 'RIght' true
//Ts添加了枚举,使用枚举类型可以为一组数值赋予友好的名字。
enum Color{Red=1,Yellow,Blue,Green};
console.log(Color.Red);//1
console.log(Color.Blue);//3
let Apple:Color=Color.Red;
console.log(Apple);//1
//枚举类型提供的一个便利是你可以由枚举的值得到它的名字。
console.log(Color[4]);//Green
let Orange:string=Color[3];
console.log(Orange);//Bule
//any类型,这个类型可以接受任意参数,在编译的时候可以移除类型检查
let data:any=[1,'h',false];//元组
console.log(data);//[ 1, 'h', false ]
//never,void等作用不大的函数。
编译成js为如下代码:
//元组也是数组,但允许装载不同的元素
var tur = [19, "RIght", true];
console.log(...tur);
//Ts添加了枚举,使用枚举类型可以为一组数值赋予友好的名字。
var Color;
(function (Color) {
Color[Color["Red"] = 1] = "Red";
Color[Color["Yellow"] = 2] = "Yellow";
Color[Color["Blue"] = 3] = "Blue";
Color[Color["Green"] = 4] = "Green";
})(Color || (Color = {}));
;
console.log(Color.Red);
console.log(Color.Blue);
let Apple = Color.Red;
console.log(Apple);
//枚举类型提供的一个便利是你可以由枚举的值得到它的名字。
console.log(Color[4]);
let Orange = Color[3];
console.log(Orange);
//any类型,这个类型可以接受任意参数,在编译的时候可以移除类型检查
let data = [1, 'h', false]; //元组
console.log(data);
//never,void等作用不大的函数。
3.0、类型推断
如果没有指明类型,ts能根据值自动推断出数据类型。
//类型推断
let i=1;
console.log(typeof(i));//number
4.0、泛型
很多语言都提供了泛型,在某些情况下,泛型可以提高代码重用率,规范代码.
数组使用泛型非常普遍。
var arr:number[]=[1,2,3,3,5];
var arrs:Array<Number>=[0,3];//泛型
console.log(...arr);//数组中的元素
console.log(...arrs);//数组中的元素
console.log(arr);//打印该数组
console.log(arrs);//打印该数组
//1 2 3 3 5
//0 3
//[ 1, 2, 3, 3, 5 ]
//[ 0, 3 ]
数组泛型使用管道|则变成了元组。
var T:Array<string|Number>=[19,'jim'];
console.log(...T);
//19 'jim'
如果泛型使用在通用的函数中,那么不论传入的参数是何类型,都能返回原本的数据类型。
而any虽然也有这样的效果,却不严谨,any接受任意数据可能会丢失数据。
function show<T>(args:T):T{
return args;
}
console.log(show("Tom"));
console.log(show(3));
console.log(show(false));
//Tom
//3
//false
4.1、泛型转换
泛型转换可以对一个空对象进行泛型声明。
let n={};
let n=<string>{};
5.0、函数
关于函数部分特性,如允许参数默认值存在是从ES6中移植过来。
//函数返回类型及参数类型都需要
function sum(firstnum:Number,lastnum:any):Number{
return firstnum+lastnum;
}
let s=sum(1,2);
console.log(s);//3
//允许默认值存在
function sums(firstnum:Number,lastnum:any=8):Number{
return firstnum+lastnum;
}
//传入参数,未传入的参数由默认参数代替
var ss=sums(1);
console.log(ss);//9
//传入所有参数
var sss=sums(1,6);
console.log(sss);//7
6.0、类
类的特性也是从ES6中反向移植过来,并且增强了支持。语法非常类似java。
提供了访问级别:public\protected\privite,默认为public。
提供了static、readonly关键字,声明为类成员、只读属性。
提供了存取器来修改私有数据,类似于某种框架提供的计算属性。
class Person {
_name:string;//默认为public
private _age:Number=0;//构造方法中没有引用,会警告,可以没有初始值
static id:Number=0;//静态属性,类名调用
readonly gender:string="male";//只读属性,实例调用
//构造方法
constructor(name:string) {
this._name=name;
}
//存取器,只带有 get不带有 set的存取器自动被推断为 readonly
get age():Number{
return this._age;
}
//本方法使用类似属性
set age(_age:Number){
console.log("set方法被调用。。。");
this._age=_age;
}
//普通方法,默认public
show(){
console.log("name:"+this._name+" age:"+this._age+" id:"+Person.id);
}
static i=0;
//静态方法,类名调用
static display(){
Person.i=Person.i+1;
console.log("i的值:"+Person.i);
}
}
测试一下:
let p=new Person("jack");
p.age=19;//存取器修改数值
p.show();
//静态属性
Person.id=8;
p.show();
console.log(p.gender);
//静态方法
Person.display();
Person.display();
Person.display();
let p2=new Person("Tom");
p.show();
//如下:
set方法被调用。。。
name:jack age:19 id:0
name:jack age:19 id:8
male
i的值:1
i的值:2
i的值:3
name:jack age:19 id:8
ts编译为js代码如下:
基本是ES6的风格:
class Person {
//构造方法
constructor(name) {
this._age = 0; //构造方法中没有引用,会警告,可以没有初始值
this.gender = "male"; //只读属性,实例调用
this._name = name;
}
//存取器,只带有 get不带有 set的存取器自动被推断为 readonly
get age() {
return this._age;
}
//本方法使用类似属性
set age(_age) {
console.log("set方法被调用。。。");
this._age = _age;
}
//普通方法,默认public
show() {
console.log("name:" + this._name + " age:" + this._age + " id:" + Person.id);
}
//静态方法,类名调用
static display() {
Person.i = Person.i + 1;
console.log("i的值:" + Person.i);
}
}
Person.id = 0; //静态属性,类名调用
Person.i = 0;
7.0、继承
Ts继承也完全符合Es6的语法。使用extends关键字继承,super关键字表示引用父类方法,不支持多继承,支持多重继承。
方便演示,给父类增加一个ha()方法。
ha(){
console.log("hahah....");
}
子类继承父类:
class Man extends Person{//关键字extends表示继承,不支持多继承
constructor(name:string) {
super(name);//构造方法必须使用super关键字表示引用父类
}
//重写子类方法
show(){
console.log("..........");
}
}
var Tom =new Man("Tom");
Tom.show();
Tom.ha();//调用父类方法
var Jack:Person=new Man("Jack");//多态,父类接收子类实例
Jack.show();
Jack.ha();
..........
hahah....
..........
hahah....
这段代码基本和es6语法差不多,翻译成Js代码基本没有变化。
8.0、接口
接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现。
TS提供了接口,方便我们约束类的行为,很多语言都提供了接口,TS的接口不仅约束行为,对属性也提供约束。
关键字使用interface声明接口,接口是TS的一部分,不能编译成js代码。
interface address{
country:string
city:string
town?:string//可选属性
other():any;//方法
}
let Tom:address={country:"China",city:"Shanghai",other(){return "Hello"+this.country}}
console.log(Tom);
console.log(Tom.other());
{ country: 'China', city: 'Shanghai', other: [Function: other] }
HelloChina
8.1、接口继承
接口继承就是说接口可以通过其他接口来扩展自己,Typescript 允许接口继承多个接口,继承使用关键字 extends,继承的接口需要实现父接口中所有约束。
//接口继承
interface IDcard extends address{
id:string|Number;//联合
show:()=>string;//箭头函数
}
let Jack:IDcard={id:"109999",show(){return "show "+this.city},
country:"China",city:"Shanghai",other(){return "Hello"+this.country}};
console.log(Jack);
console.log(Jack.show());
{ id: '109999',
show: [Function: show],
country: 'China',
city: 'Shanghai',
other: [Function: other] }
show Shanghai
8.2、类实现接口
接口虽然可以直接使用对象赋值,但最常用的还是在类中,TS实现类需要显式的使用implements关键字。
//类实现接口
class Dave implements address {
country: string="USA";
city: string="NewYork";
town?: string | undefined;
other() {
console.log("Hello implements and hello "+this.country+this.city);
}
}
let dave=new Dave();
dave.other();
//Hello implements and hello USANewYork
8.3、接口继承类
TS中接口也可以继承类,使用extends关键字声明。
当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)
//接口继承类
interface Sally extends Dave{
name:string;
}
let sally:Sally={name:"sally",country:"England",city:"",other(){return "Sally "+this.country}};
console.log(sally);
console.log(sally.other());
{ name: 'sally',
country: 'England',
city: '',
other: [Function: other] }
Sally England
8.4、可索引属性
类似数组,不过TypeScript支持两种索引签名:字符串和数字。值可以是多种类型,不过一旦使用所以,接口中其他属性需要和索引值的类型有所关联,为其子类型。
interface arr1{
//索引
[index:number]:string;
}
interface arr2{
//索引
[index:string]:number;
}
interface NumberDictionary {
[index: string]: number;
length: number; // 可以,length是number类型
name: string // 错误,`name`的类型与索引类型返回值的类型不匹配
}
//索引为number
let arrnum:arr1=["dava","sally","tom"];
console.log(arrnum[0]);
arrnum[6]="jack";
console.log(arrnum);
//索引为string
let arrstr:arr2={"tom":1,"sally":2};
console.log(arrstr);
console.log(arrstr["tom"])
console.log(arrstr.sally)
dava
[ 'dava', 'sally', 'tom', <3 empty items>, 'jack' ]
{ tom: 1, sally: 2 }
1
2
9.0、模块
从ECMAScript 2015开始,JavaScript引入了模块的概念。TypeScript也沿用这个概念。
模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export
形式之一导出它们。 相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import
形式之一。
模块是自声明的;两个模块之间的关系是通过在文件级别上使用imports和exports建立的。
module.ts文件,导出。。
class MD1 {
name:string='module';
constructor(){
}
show(){
console.log("this is "+this.name);
}
}
function hello() {
console.log("hello")
}
export{MD1};
export{hello}
导入
import {MD1,hello} from './module'
let m=new MD1();
m.show()
hello;//直接使用函数名调用?
执行顺序。。。。
hello
this is module
10.0、命名空间
TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”。
命名空间也是用来处理命名冲突问题,主要用于文件内冲突。使用namespace关键字声明。
内部使用:
name.ts文件:
namespace A{
export class NS {
name:string='namespace';
constructor(){
}
show(){
console.log("this is "+this.name);
}
}
function hello() {
console.log("hello")
}
//只能在命名空间范围内书写
var ns =new NS();
ns.show();
hello();
}
//this is namespace
//hello
外部使用
命名空间可以有多个,里面的数据不能在外部使用,如果在外部使用,需要export导出使用的部分,使用命名空间名字.模块访问。
namespace A{
export class NS {
name:string='namespace';
constructor(){
}
show(){
console.log("this is "+this.name);
}
}
function hello() {
console.log("hello")
}
//只能在命名空间范围内书写
var ns =new NS();
ns.show();
hello();
}
//NS类被导出,可以使用
var ns =new A.NS()
ns.show();
文件外使用:
如果需要导出到其他文件,同样作为模块在其他文件中导入。
space.ts文件:
import {A} from './name'
var a=new A.NS();//命名空间名需要
a.show();
A.hello();//函数使用
11.0、装饰器
Javascript里的装饰器目前处在 [建议征集的第二阶段],但在TypeScript里已做为一项实验性特性予以支持。
注意 装饰器是一项实验性特性,在未来的版本中可能会发生改变。
首先装饰器需要以下支持:
ES5以上,允许Decortor,在配置文件中查看:
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
装饰器有多种,如类装饰器、访问器装饰器、属性装饰器、方法装饰器、参数装饰器,装饰器装饰级别也分装饰器和装饰器工厂。
装饰器就是一个函数,可以接受参数,接受的参数即被修饰的信息,在被修饰的位置加上@函数名.
简单看一下类装饰器:
//装饰器
function dark(params:any) {
console.log(params);//传入类
console.log(params.id);//无法访问
params._color();//静态方法可以被访问
}
//声明使用
@dark
class Dog{
name:string='dog';
id:string="001"
constructor(){
}
show(){
console.log("hello this is "+this.name);
}
hello(){
console.log("Hello")
}
static _color(){
console.log("the color is yellow")
}
}
let dog=new Dog();
dog.show();
结果如下:
[Function: Dog]
undefined
the color is yellow
hello this is dog
类装饰器传入的参数即类本身。
执行时机
修饰器对类的行为的改变,是代码编译时发生的(不是TypeScript编译,而是js在执行机中编译阶段),而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。
注意: 装饰器是实验特性。