本文首发在个人我的博客:muyunyun.cn/posts/66a54…
文中的案例代码已经上传到 TypeScriptgit
TypeScript 并非一个彻底新的语言, 它是 JavaScript 的超集,为 JavaScript 的生态增长了类型机制,并最终将代码编译为纯粹的 JavaScript 代码。程序员
TypeScript 由 Microsoft(算上 Angular 2 的话加上 Google)开发和维护的一种开源编程语言。 它支持 JavaScript 的全部语法和语义,同时经过做为 ECMAScript 的超集来提供一些额外的功能,如类型检测和更丰富的语法。下图显示了 TypeScript 与 ES5,ES2015,ES2016 之间的关系。github
JavaScript 是一门弱类型语言,变量的数据类型具备动态性,只有执行时才能肯定变量的类型,这种后知后觉的认错方法会让开发者成为调试大师,但无益于编程能力的提高,还会下降开发效率。TypeScript 的类型机制能够有效杜绝由变量类型引发的误用问题,并且开发者能够控制对类型的监控程度,是严格限制变量类型仍是宽松限制变量类型,都取决于开发者的开发需求。添加类型机制以后,反作用主要有两个:增大了开发人员的学习曲线,增长了设定类型的开发时间。整体而言,这些付出相对于代码的健壮性和可维护性,都是值得的。typescript
此外,类型注释是 TypeScript 的内置功能之一,容许文本编辑器和 IDE 能够对咱们的代码执行更好的静态分析。 这意味着咱们能够经过自动编译工具的帮助,在编写代码时减小错误,从而提升咱们的生产力。编程
对 TypeScript 的简介到此,接下来对其特有的知识点进行简单归纳总结,(网上不少教程实际上把 ES6, ES7 的知识点也算进 ts 的知识点了,固然这没错~)数组
一个保存字符串的文本,类型声明为 string。能够发现类型声明可大写也可小写,后文同理。浏览器
let name: string = 'muyy'
let name2: String = 'muyy'复制代码
boolean是 true 或 false 的值,因此 let isBool3: boolean = new Boolean(1)
就会编译报错,由于 new Boolean(1) 生成的是一个 Bool 对象。编程语言
let isBool1: boolean = false复制代码
let number: number = 10;复制代码
数组是 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"];复制代码
列出全部可用值,一个枚举的默认初始值是0。你能够调整一开始的范围:ide
enum Role {Employee = 3, Manager, Admin}
let role: Role = Role.Employee
console.log(role) // 3复制代码
any 是默认的类型,其类型的变量容许任何类型的值:
let notSure:any = 10;
let notSure2:any[] = [1,"2",false];复制代码
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, "人"); // 鸣人复制代码
传统的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 = 23
console.log(hello.name); // muyy复制代码
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("鸣人","鸣")); // true
console.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;复制代码
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);
}
}复制代码
软件工程中,咱们不只要建立一致的定义良好的 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);复制代码