什么是typeScript?
typeScript简称ts . TypeScript 扩展了 JavaScript ,为它添加了类型支持。TypeScript 可以在您运行代码之前找到错误并提供修复,从而改善您的开发体验。TypeScript 代码通过 TypeScript 编译器或 Babel 转译为 JavaScript 代码,生成后的代码也是干净,简洁,运行在任何可以运行 JavaScript 的地方,无论是在浏览器,Node.JS 还是在您的应用中。
文档的主要语法很多,我们不会在这里详细的描述每一个细节,但在项目中主要体现的 基础类型, 接口 , 类,函数,泛型,高级类型中的全部或部分功能,我们会加以使用和说明 . 之后就可以非常容易的上手ts开发, 当然如果遇到没见过的语法,查文档即可.
5分钟上手typeScript
先抛开and集成框架,从基本的typeScript文件开始, 创建tests/tsTest.ts
文件,代码示例: 链接 .
安装ts解析器
npm install -g typescript
编译代码
tsc greeter.ts
知识重点
typeScript的知识点很多,但很多在开发中可能很少会遇到或者目前还用不到. 这里列举几个大家在开发中一定要用到的知识点,减少学习负担. 如果在开发中遇到了没见过的知识点,再回头查文档.
为了让程序有价值,我们需要能够处理最简单的数据单元:数字,字符串,结构体,布尔值等。 TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。
布尔值
let isDone: boolean = false;
数字
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
字符串
let name: string = "bob";
name = "smith";
数组
let list: number[] = [1, 2, 3];
// 第二种方式是使用数组泛型,Array<元素类型>:
let list: Array<number> = [1, 2, 3];
元组 Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为string和number类型的元组。
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
枚举
enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
Any
有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用any类型来标记这些变量:
Object类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法:
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
let list: any[] = [1, true, "free"];
// Object类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法:
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
Void
function warnUser(): void {
console.log("This is my warning message");
}
//声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null:
let unusable: void = undefined;
Null 和 Undefined
TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null。 和void相似,它们的本身的类型用处不是很大:
经常用的地方就是*联合类型 *string | null | undefined
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
Never
never类型表示的是那些永不存在的值的类型。 例如,never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是never类型,当它们被永不为真的类型保护所约束时。
never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使any也不可以赋值给never。
Object
object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型。
类型断言
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有as语法断言是被允许的。
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// 另一个为as语法:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
ECMAScript 6开始,JavaScript程序员将能够使用基于类的面向对象的方式。 使用TypeScript,我们允许开发者现在就使用这些特性,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。
类
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
在类里面,使用()形式和箭头函数形式定义方法是有去别的. 我们这里建议使用原型的形式定义方法.
继承
在TypeScript里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
公共,私有与受保护的修饰符
默认为public
在上面的例子里,我们可以自由的访问程序里定义的成员。 如果你对其它语言中的类比较了解,就会注意到我们在之前的代码里并没有使用public来做修饰;例如,C#要求必须明确地使用public指定成员是可见的。 在TypeScript里,成员都默认为public。
你也可以明确的将一个成员标记成public。 我们可以用下面的方式来重写上面的Animal类:
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
理解private
当成员被标记成private时,它就不能在声明它的类的外部访问。
即时是继承的对象,依然无法直接访问.
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
say(){
// 这里无法访问 父类中的 private name属性
console.log('this.name:',this.name);
}
}
new Animal("Cat").name; // 错误: 'name' 是私有的.
let animal = new Animal("Goat");
let rhino = new Rhino();
理解protected
protected修饰符与private修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问。
class Animal {
protected name: string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
say(){
// 可以访问 父类 protected name属性
console.log('this.name:',this.name);
}
}
new Animal("Cat").name; // 错误: 'name' 是私有的.
let animal = new Animal("Goat");
let rhino = new Rhino();
readonly修饰符
你可以使用readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.
存取器
暂不处理.
静态属性
类的静态成员,这些属性存在于类本身上面而不是类的实例上。
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
抽象类
暂不处理.
把类当做接口使用
类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类。
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};