TypeScript 简介
TypeScript 由 Microsoft(算上 Angular 2 的话加上 Google)开发和维护的一种开源编程语言。 它支持 JavaScript 的所有语法和语义同时通过作为 ECMAScript 的超集来提供一些额外的功能如类型检测和更丰富的语法。下图显示了 TypeScript 与 ES5ES2015ES2016 之间的关系。
回到顶部(go to top)
使用 TypeScript 的原因
JavaScript 是一门弱类型语言变量的数据类型具有动态性只有执行时才能确定变量的类型这种后知后觉的认错方法会让开发者成为调试大师但无益于编程能力的提升还会降低开发效率。TypeScript 的类型机制可以有效杜绝由变量类型引起的误用问题而且开发者可以控制对类型的监控程度是严格限制变量类型还是宽松限制变量类型都取决于开发者的开发需求。添加类型机制之后副作用主要有两个增大了开发人员的学习曲线增加了设定类型的开发时间。总体而言这些付出相对于代码的健壮性和可维护性都是值得的。
此外类型注释是 TypeScript 的内置功能之一允许文本编辑器和 IDE 可以对我们的代码执行更好的静态分析。 这意味着我们可以通过自动编译工具的帮助在编写代码时减少错误从而提高我们的生产力。
对 TypeScript 的简介到此接下来对其特有的知识点进行简单概括总结(网上很多教程实际上把 ES6 ES7 的知识点也算进 ts 的知识点了当然这没错~)
回到顶部(go to top)
数据类型
String 类型
一个保存字符串的文本类型声明为 string。可以发现类型声明可大写也可小写后文同理。
let name: string = 'muyy'let name2: String = 'muyy'
Boolen 类型
boolean是 true 或 false 的值所以 let isBool3: boolean = new Boolean(1) 就会编译报错因为 new Boolean(1) 生成的是一个 Bool 对象。
let isBool1: boolean = false
Number 类型
let number: number = 10;
Array 类型
数组是 Array 类型。然而因为数组是一个集合我们还需要指定在数组中的元素的类型。我们通过 Array<type> or type[] 语法为数组内的元素指定类型
let arr:number[] = [1, 2, 3, 4, 5];let arr2:Array<number> = [1, 2, 3, 4, 5];let arr3:string[] = ["1","2"];let arr4:Array<string> = ["1","2"];
Enums 类型
列出所有可用值一个枚举的默认初始值是0。你可以调整一开始的范围
enum Role {Employee = 3, Manager, Admin}let role: Role = Role.Employeeconsole.log(role) // 3
Any 类型
any 是默认的类型其类型的变量允许任何类型的值
let notSure:any = 10;let notSure2:any[] = [1,"2",false];
Void 类型
JavaScript 没有空值 Void 的概念在 TypeScirpt 中可以用 void 表示没有任何返回值的函数
function alertName(): void { console.log('My name is muyy') }
函数
为函数定义类型
我们可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript能够根据返回语句自动推断出返回值类型因此我们通常省略它。下面函数 add, add2, add3 的效果是一样的其中是 add3 函数是函数完整类型。
function add(x: string, y: string): string{ return "Hello TypeScript"; }let add2 = function(x: string, y: string): string{ return "Hello TypeScript"; }let add3: (x: string, y: string) => string = function(x: string, y: string): string{ return "Hello TypeScript"; }
可选参数和默认参数
JavaScript 里每个参数都是可选的可传可不传。 没传参的时候它的值就是 undefined 。 在 TypeScript 里我们可以在参数名旁使用?实现可选参数的功能。 比如我们想让 lastname 是可选的
function buildName(firstName: string, lastname?: string){ console.log(lastname ? firstName + "" + lastname : firstName) }let res1 = buildName("鸣","人"); // 鸣人let res2 = buildName("鸣"); // 鸣let res3 = buildName("鸣", "人", "君"); // Supplied parameters do not match any signature of call target.
如果带默认值的参数出现在必须参数前面用户必须明确的传入 undefined 值来获得默认值。 例如我们重写上例子让 firstName 是带默认值的参数
function buildName2(firstName = "鸣", lastName?: string){ console.log(firstName + "" + lastName) }let res4 = buildName2("人"); // undefined人let res5 = buildName2(undefined, "人"); // 鸣人
回到顶部(go to top)
传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件但对于熟悉使用面向对象方式的程序员来讲就有些棘手因为他们用的是基于类的继承并且对象是由类构建出来的。 从ECMAScript 2015也就是ECMAScript 6开始JavaScript程序员将能够使用基于类的面向对象的方式。 使用TypeScript我们允许开发者现在就使用这些特性并且编译后的JavaScript可以在所有主流浏览器和平台上运行而不需要等到下个JavaScript版本。
class Person{ name:string; // 这个是对后文this.name类型的定义 age:number; constructor(name:string,age:number){ this.name = name; this.age = age; } print(){ return this.name + this.age; } }let person:Person = new Person('muyy',23)console.log(person.print()) // muyy23
我们在引用任何一个类成员的时候都用了 this。 它表示我们访问的是类的成员。其实这本质上还是 ES6 的知识只是在 ES6 的基础上多上了对 this 字段和引用参数的类型声明。
继承
class Person{ public name:string; // public、private、static 是 typescript 中的类访问修饰符 age:number; constructor(name:string,age:number){ this.name = name; this.age = age; } tell(){ console.log(this.name + this.age); } }class Student extends Person{ gender:string; constructor(gender:string){ super("muyy",23); this.gender = gender; } tell(){ console.log(this.name + this.age + this.gender); } }var student = new Student("male"); student.tell(); // muyy23male
这个例子展示了 TypeScript 中继承的一些特征可以看到其实也是 ES6 的知识上加上类型声明。不过这里多了一个知识点 —— 公共私有以及受保护的修饰符。TypeScript 里成员默认为 public 当成员被标记成 private 时它就不能在声明它的类的外部访问protected 修饰符与private 修饰符的行为很相似但有一点不同protected 成员在派生类中仍然可以访问。
存储器
TypeScript 支持通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
对于存取器有下面几点需要注意的
首先存取器要求你将编译器设置为输出 ECMAScript 5 或更高。 不支持降级到 ECMAScript 3。 其次只带有 get 不带有 set 的存取器自动被推断为 readonly。 这在从代码生成 .d.ts 文件时是有帮助的因为利用这个属性的用户会看到不允许够改变它的值。
class Hello{ private _name: string; private _age: number; get name(): string { return this._name; } set name(value: string) { this._name = value; } get age(): number{ return this._age; } set age(age: number) { if(age>0 && age<100){ console.log("年龄在0-100之间"); // 年龄在0-100之间 return; } this._age = age; } }let hello = new Hello(); hello.name = "muyy"; hello.age = 23console.log(hello.name); // muyy
回到顶部(go to top)
接口
接口
TypeScript的核心原则之一是对值所具有的结构进行类型检查。在TypeScript里接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
interface LabelValue{ label: string; }function printLabel(labelObj: LabelValue){ console.log(labelObj.label); }let myObj = { "label":"hello Interface"}; printLabel(myObj);
LabelledValue 接口就好比一个名字它代表了有一个 label 属性且类型为 string 的对象。只要传入的对象满足上述必要条件那么它就是被允许的。
另外类型检查器不会去检查属性的顺序只要相应的属性存在并且类型也是对的就可以。
可选属性
带有可选属性的接口与普通的接口定义差不多只是在可选属性名字定义的后面加一个 ? 符号。可选属性的好处之一是可以对可能存在的属性进行预定义好处之二是可以捕获引用了不存在的属性时的错误。
interface Person{ name?:string; age?:number; }function printInfo(info:Person){ console.log(info); }let info = { "name":"muyy", "age":23}; printInfo(info); // {"name": "muyy", "age": 23}let info2 = { "name":"muyy"}; printInfo(info2); // {"name": "muyy"}
函数类型
接口能够描述 JavaScript 中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外接口也可以描述函数类型。定义的函数类型接口就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。定义后完成后我们可以像使用其它接口一样使用这个函数类型的接口。
interface SearchFunc{ (source: string, subString: string): boolean; }let mySearch: SearchFunc; mySearch = function(source: string,subString: string){ return source.search(subString) !== -1; };console.log(mySearch("鸣人","鸣")); // trueconsole.log(mySearch("鸣人","缨")); // false
可索引类型
与使用接口描述函数类型差不多我们也可以描述那些能够“通过索引得到”的类型比如 a[10] 或 ageMap["daniel"]。 可索引类型具有一个索引签名它描述了对象索引的类型还有相应的索引返回值类型。 让我们看如下例子
interface StringArray{ [index: number]: string; }let MyArray: StringArray; MyArray = ["是","云","随","风"];console.log(MyArray[2]); // 随
类类型
与 C# 或 Java 里接口的基本作用一样TypeScript 也能够用它来明确的强制一个类去符合某种契约。
我们可以在接口中描述一个方法在类里实现它如同下面的 setTime 方法一样
interface ClockInterface{ currentTime: Date; setTime(d: Date); }class Clock implements ClockInterface{ currentTime: Date; setTime(d: Date){ this.currentTime = d; } constructor(h: number, m: number) {} }
继承接口
和类一样接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里可以更灵活地将接口分割到可重用的模块里。
interface Shape{ color: string; }interface PenStroke{ penWidth: number; }interface Square extends Shape,PenStroke{ sideLength: number; }let s = <Square>{}; s.color = "blue"; s.penWidth = 100; s.sideLength = 10;
回到顶部(go to top)
模块
TypeScript 与 ECMAScript 2015 一样任何包含顶级 import 或者 export 的文件都被当成一个模块。
export interface StringValidator{ isAcceptable(s:string): boolean; }var strReg = /^[A-Za-z]+$/;var numReg = /^[0-9]+$/;export class letterValidator implements StringValidator{ isAcceptable(s:string): boolean{ return strReg.test(s); } }export class zipCode implements StringValidator{ isAcceptable(s: string): boolean{ return s.length == 5 && numReg.test(s); } }
回到顶部(go to top)
泛型
软件工程中我们不仅要创建一致的定义良好的 API 同时也要考虑可重用性。 组件不仅能够支持当前的数据类型同时也能支持未来的数据类型这在创建大型系统时为你提供了十分灵活的功能。
在像 C# 和 Java 这样的语言中可以使用泛型来创建可重用的组件一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
初探泛型
如下代码我们给 Hello 函数添加了类型变量 T T 帮助我们捕获用户传入的类型比如string。我们把这个版本的 Hello 函数叫做泛型因为它可以适用于多个类型。 代码中 output 和 output2 是效果是相同的第二种方法更加普遍利用了类型推论 —— 即编译器会根据传入的参数自动地帮助我们确定T的类型
function Hello<T>(arg:T):T{ return arg; }let outPut = Hello<string>('Hello Generic');let output2 = Hello('Hello Generic')console.log(outPut);console.log(outPut2);