TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。 TypeScript扩展了JavaScript的句法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作。TypeScript是为大型应用之开发而设计,而编译时它产生 JavaScript 以确保兼容性。 TypeScript 支持为已存在的 JavaScript 库添加类型信息的头文件,扩展了它对于流行的库如 jQuery,MongoDB,Node.js 和 D3.js 的支持。
一、数据类型
TypeScript数据类型包括:Boolean, Number, String, Array, Enum, Any,Void七种
1. Boolean类型,取值true/false
let isDone: boolean = false;
2. Number类型
跟JavaScript一样,TypeScript所有的数值类型采用浮点型计数,其类型为‘number’。
let height: number = 6.32322;
3. String类型
TypeScript和JavaScript一样也是用双引号(“)或者单引号包裹文本数据。
let name: string = "bob";
4. Array类型
在TypeScript中如JavaScript一样允许我们操结合操作。数组类型可以使用下边两种方式之一。第一种方式,你可以在数据类型之后带上’[ ]‘:
let list:number[] = [1, 2, 3];
第二种方式,也可以采用泛型的数组类型:
let list:Array<number> = [1, 2, 3];
5. 枚举Enum类型
TypeScript为JavaScript新增了枚举这种标准的集合数据类型。和在c#中一样,枚举是为一组数值类型一组更友好的名称:
// 定义枚举
enum Color {Red, Green, Blue};
// 引用枚举
let c: Color = Color.Green;
或是手动设置全部的枚举成员:
enum Color {Red = 1, Green = 2, Blue = 4};
let c: Color = Color.Green;
运用枚举类型可以很容易从一个数值类型获取对应枚举名称。例如我们有一个数值类型2,但不确认将匹配哪一个枚举成员,那么我们可以如下使用:
enum Color {Red = 1, Green, Blue};
let colorName: string = Color[2];
console.log(colorName);
6. Any类型
有时我们需要描述一些我们不知道的出现于应用中的动态数据类型,这可能来自第三方用户或者lib。在这里我们希望该数据不要加入TypeScript的类型检查,并且能跳过编译检查。为此我们可以采用‘any’类型标注:
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;
‘any’类型是一种强大的兼容存在的JavaScript库的类型系统。他允许跳过TypeScript的编译时类型的检查。 ‘any’类型对应于我们只知道部分数据类型,但是不是所有的数据类型的类型系统。例如一个混合了多种类型的集合数组。
let list:any[] = [1, true, "free"];
list[1] = 100;
7. Void类型
和‘any’相对的数据类型则是’Void‘,它代表没有任何数据类型。通常用于表示一个方法没有任何返回值:
warnUser(): void {
console.log("This is my warning message");
}
二、运算符与表达式
与JavaScript完全相同,需要注意的是临时变量需要加上数据类型
let someValPoint: Point = new Point();
三、对像类型和接口
1.接口定义:使用关键字interface+接口名,例如:
interface Point {
x: number;
y: number;
}
2.接口继承:使用extends关键字,例如:
interface ThreeDPoint extends Point {
z: number;
}
3.对像类型:与JavaScript定义方式相同,例如:
const pointObjectFirst = {
x: 2,
y: 1
};
const pointObjectSecond = {
x: 3,
y: 32
};
const threeDPoint = {
x: 20,
y: 11,
z: 50
};
4.接口定义数据与obj对像数据调用比较:
// obj数据类型调用
addPoints(pointObjectFirst, pointObjectSecond);
// 接口类型数据调用,假设interfaceObjFirst,interfaceObjSecond,是两个已实现接口Point的类,那么实例化之后调用方式为:
addPoints(interfaceObjFirst, interfaceObjSecond);
/**
* 测试方法
* @param pointOne
* @param pointTwo
* @returns {{x: any, y: any}}
*/
addPoints(
pointOne: any,
pointTwo: any) {
return {
x: p1.x + p2.x,
y: p1.y + p2.y
};
}
从上面例子来看,如果通过{}定义的数据,缺少某个属性,比如x,ide不会报错,但如果使用接口,则无法往下编译。另外需要补充的是:接口的基本用途,规定子类的行为,接口在实际开发过程中最大好处在于我们可以按照设计,先把接口写好,然后大家干活的时候,可以用写好的接口去实现他们的具体功能,避免用{}方式定义有可能某些属性没有而报错,如果用接口定义,排错功能在写代码时ide已帮我们查找到了,所以这样能提高一些工作效率。
四、类,使用class 关键字,与es6, java相同
// 定义类
class Point {
/*定义静态属性*/
static origin = new Point(0, 0);
/*定义静态方法*/
static getRule(p1: Point, p2: Point) {
return p1.add(p2);
}
/*构造体*/
constructor(private xValue: number,
private yValue: number) {
}
/*定义set*/
get x() {
return this.xValue;
}
/*定义set*/
set x(value: number) {
this.xValue = value;
}
/*定义方法(默认公有,可不写public关键字)*/
add(point: Point) {
return new Point(point.x + this.x, point.y + this.y);
}
/*定义方法(显式声明公有,写public关键字)*/
public addSides(point: Point) {
return new Point(point.x + this.x, point.y + this.y);
}
/*定义方法(私有,写private关键字)*/
private getArea(point: Point) {
return new Point(point.x + this.x, point.y + this.y);
}
}
// 类的实例化及方法调用
let pointAdder:Point = new Point(2, 1);
let pointAdded:Point = new Point(23,32);
let addValue:number = pointAdded.add(pointAdder);
let pointStatic = Point.getRule(p1, p2);
注意:与es5不同,TypeScript舍去了定义方法时使用的function关键字,这样更简洁。同时支持private私有成员,更方便信息隐藏。
五、继承
继承可以简化对类定义的描述,提高代码重用,是多态的基础。以下以图型Shape类作演示
/*多边形基类定义*/
class Polygon {
height: number;
/*构造方法*/
constructor(height: number) {
this.height = height;
}
/*定义公有方法*/
public computeArea(): number {
return 0;
}
/*定义私有方法*/
private getSides(): number {
return 0;
}
}
/*三角形继承自多边形基类,拥用基类public属性和方法*/
class Triangle extends Polygon {
/*自定义属性*/
base: number;
/*构造体,初始化自已还通过super调用了父类构造体*/
constructor(height: number, base: number) {
super(height);
this.base = base;
}
/*重写父类公用方法,即俗称同样方法名子类有自已的实现*/
public computeArea() {
return .5 * this.base * this.height;
}
}
/*长方形继承自多边形基类,拥用基类public属性和方法,也定义了自身新的属性*/
class Rectangle extends Polygon {
/*新的属性*/
width: number;
/*构造体,初始化自已还通过super调用了父类构造体*/
constructor(height: number, width: number) {
super(height);
this.width = width;
}
/*重写父类公有方法*/
public computeArea() {
return this.height * this.width;
}
}
/*正方形继承自长方形类*/
class Square extends Rectangle {
/*构造体,初始化自已还通过super调用了父类(Rectangle)构造体*/
constructor(length: number) {
super(length, length);
}
/*重写父类公有方法,加入了自身不同于父类的实现*/
public computeArea() {
return this.width * this.width;
}
}
六、接口实现
/*定义接口*/
interface Shape {
/*定义接口模板方法,实现此接口的类必须实现此接口的所有具体方法,否则无法编译*/
computeArea: () => number;
}
/*实现Shape接口,支持多接口实现*/
class Polygon implements Shape {
height: number;
/*实现接口方法,由每个类具体实现,内容可以不同,但方法体必须相同*/
computeArea() {
return 0;
}
}
/*实现Shape接口*/
class Elipse implements Shape {
a: number;
b: number;
constructor(a: number, b: number) {
this.a = a;
this.b = b;
}
/*实现接口方法,由每个类具体实现,内容可以不同,但方法体必须相同*/
computeArea() {
return Math.PI * this.a * this.b;
}
}
使用接口编程,类之间通信简单易懂,扩展性好,能提高复用、扩展性,耦合松散,适合于大中型项目模块代码质量控制。
七、方法和重载
class OverLoad {
/*定义方法foo,使用number型的2个参数,返回number类型结果*/
foo(a: number, b: number): number;
/*定义方法foo,使用string型的2个参数,返回string类型结果(发生重载行为)*/
foo(a: string, b: string): string;
/*定义方法foo,使用any型的2个参数,返回any类型结果(发生重载行为)*/
foo(a:any, b:any): any {
if (typeof a == "string" && typeof b == "string") {
return a + " " + b;
}
return a + b;
}
}
let ol:OverLoad = new OverLoad();
ol.foo(1, 2); // 直接调用,调用了number类型参数的方法
ol.foo('xxd','xxd'); // 重截调用,调用了string类型参数的方法
请注意:typescript目前只支持等量参数重载,不支持,相同方法名,不同参数数量和类型的重载。
八、泛型
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。
泛型典型应用即在集合类数据类型中。例如,要定义个类似Map的类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象。
/*定义泛型一般使用T*/
foo<T>(a: T) {
return a;
}
// 泛型运用
let bar:any = foo<string>("hello world");
let barNumber:any = foo<number>(10);
let barElement:any = foo<HTMLElement>(document.createElement("div"));
let bazArray:any = new Array<string>();
let json = '{"firstName":"John","lastName":"Doe"}';
interface Person {
firstName: string;
lastName: string;
}
interface Person2 {
firstName: string;
lastName: string;
}
let myJSON = {
parse: function <T>(data: string) : T {
return JSON.parse(data);
}
};
let obj:any = myJSON.parse<Person>(json);
class Foo<T extends Person> {
foo: T;
bar(data: T) {
}
baz(): T {
return this.foo;
}
}
let obj2:any = new Foo<Person2>();
九、模块
1.外部模块:
模块导入:import 模块导出:export
/**
* 导出shape
* file: shapes.ts
*/
export interface Shape {
computeArea: () => number;
}
// 导入shapes接口
import shapes from "shapes";
/**
* 导出ellipse类,并实现自导入的shapes接口
* file: ellipses.ts
*/
export class Ellipse implements shapes.Shape {
a: number;
b: number;
constructor(a: number, b: number) {
this.a = a;
this.b = b;
}
computeArea() {
return Math.PI * this.a * this.b;
}
}
/**
* 导出继承类
* file: ellipses.ts
*/
export class Circle extends Ellipse {
radius: number;
constructor(radius: number) {
super(radius, radius);
this.radius = radius;
}
}
// file: app.ts
// 导入类
import ellipses from "ellipses";
// 实例化使用,由导入句柄名+引用句柄所引用方法
let circle = new ellipses.Circle(4);
2.内部模块
使用module、export、reference关键字组合使用
/**
* 统一定义模块使名空间Shapes
* file: shapes.ts
*/
module Shapes {
export interface Shape {
computeArea: () => number;
}
}
/**
* 引用接口类,同样位于Shapes空间中
* file: polygons.ts
*/
/// <reference path="shapes.ts" />
module Shapes {
export class Polygon implements Shape {
height: number;
constructor(height: number) {
this.height = height;
}
computeArea() {
return 0;
}
}
export class Triangle extends Polygon {
base: number;
constructor(height: number, base: number) {
super(height);
this.base = base;
}
computeArea() {
return .5 * this.base * this.height;
}
}
}
/**
* 引用,位于Shapes命名空间中内容
*/
/// <reference path="polygons.ts" />
let triangle = new Shapes.Triangle(4, 4);
建议:在模块内创建类,组织类时遵循类的单一性功能,来实现对类复杂度解耦,即一个文件对应一个类,且只包含一种功能。