为了让程序可以使用,我们需要用到一些最简单的数据单元:数字,字符串,结构,布尔值,诸如此类。在TypeScript中,支持许多正如你在JavaScript中期待的相同类型,并且弹窗提示的枚举类型很方便。
布尔值(Boolean)
最基本的数据类型是简单的true/value值,JavaScript和TypeScript(包括其他语言)把这个称作“boolean”值。
var isDone: boolean = false;
数值(Number)
和在JavaScript中一样,TypeScript中的所有数字都是浮点值。这些浮点值的类型都是“number”。
字符串(String)
在JavaScript中,页面和服务器之类创建程序的另一个基础部分是使用文本数据工作。和其它语言一样,我们使用“string”引用这些文本数据类型。就像JavaScript,TypeScript也使用双引号或单引号来包裹字符串数据。
var name: string = "bob";name = 'smith';
数组(Array)
TypeScript和JavaScript一样,允许使用数组值。数组类型有两种写法。第一种,使用数组中的元素的类型后跟中括号“[]”来标志元素类型的数组:
var list:number[] = [1, 2, 3];
第二种方式,使用泛型的数组类型,Array<elemType>:
var list:Array<number> = [1, 2, 3];
枚举(Enum)
“enum”是TypeScript对JavaScript的标准数据类型集的有用的扩展。像C#语言一样,一个枚举是一种更友好地给出数值集合的名字的方式。
enum Color {Red, Green, Blue};var c: Color = Color.Green;
默认情况下,枚举类型的成员是从0开始计数的。可以手动地通过设置成员的值来改变默认的计数规则。比如,我们可以从1开始而不是0开始:
enum Color {Red = 1, Green, Blue};var c: Color = Color.Green;
或者,手动地设置枚举的所有值(跟C#语法一样):
enum Color {Red = 1, Green = 2, Blue = 4};var c: Color = Color.Green;
枚举方便的一个特征是可以从一个名字的数值找到枚举中的值所对应的名字。比如,如果有一个值为2,但是不确定它对应到枚举中的哪一个名字,那么可以通过下面的方式查看对应的名字:
enum Color {Red = 1, Green, Blue};var colorName: string = Color[2];alert(colorName);
任意类型(Any)
有时候我们可能要描述一些我们可能不知道的变量类型。这些值可能来自动态内容,比如来自用户或者第三方类库。在这些情况下,我们要脱离类型检查和让这些值通过编译时检查,可以使用“any”进行标志:
var notSure: any = 4;notSure = "maybe a string instead";notSure = false;//最后是一个布尔值
“any”是处理现有的JavaScript的一种强有力的方式,允许慢慢地在编译期间选择加入和退出类型检查。
如果知道一部分类型但也许不是所有类型,这时“any”类型也很方便。比如,有个数组,但是这个数组混合了不同类型的值:
var list:any[] = [1, true, "free"];list[1] = 100;
也许”any”在很多方面与“void”相对,即根本不存在任何类型。通常,这个用于不返回任何值的函数的返回类型。
function warnUser(): void { alert("This is my warning message");}
TS的核心原则之一是关注值的类型检查,有时也叫“动态类型”或“结构化子类型”。在TS中,接口扮演了命名这些类型的角色,它是在你的代码内部和项目外部定义约定的强大方式。
第一个接口
了解接口如何工作的最简单的方式是开始一个简单的例子:
1 function printer(labelObj:{label:string}) { 2 console.log(labelObj.label); 3 } 4 var myObj = { size: 10, label: "size 10 object" }; 5 printer(myObj);
类型检查器会检测“printer”的调用,“printer”函数只有一个参数,该参数要求传入的对象有一个名为“label”的string类型参数。注意,实际上我们例子中传入的对象有更多的属性,但是编译器的检测机制是这样的,传入的对象的属性最少存在一个和要求的同名,并且类型匹配。
我们再次写一下这个例子,这次使用接口来描述,该接口有一个string类型的“label”属性:
1 interface LabelValue{ 2 label:string; 3 } 4 5 function printLabel(labelObj:LabelValue) { 6 document.write(labelObj.label); 7 } 8 var myObj = { size: 10, label: "size 10 object" }; 9 printLabel(myObj);
接口“LabelValue”是一个我们可以用来描述之前例子里的需求的名字。它仍表示了一个具有string类型的“label”属性。注意,我们我们传入“printLabel”方法中的对象不必像其他语言一样显示实现该接口。这里它只是要紧的模型。如果我们传入函数的对象满足列出的需求,那么传入的对象是被允许的。
值得指出的是,类型检查器对这些属性的顺序不做要求,只要求存在接口定义的这些属性,并且类型匹配。
并不是接口的所有属性都是必须要满足的。一些属性可能在一定的条件下存在或者可能根本不存在。当用户创建像”选项包”时,此时用户给一个只有两个属性要填充的函数传入一个对象,可选属性就派上用场了。
下面是此模式的一个例子:
1 function createSquare(config:SquareConfig):{color:string;area:number} { 2 var newSquare = { color: "white", area: 100 }; 3 if (config.color) { 4 newSquare.color = config.color; 5 } 6 if (config.width) { 7 newSquare.area = config.width * config.width; 8 } 9 return newSquare;10 }11 12 var mySquare = createSquare({ color: "red" });
具有可选属性的接口和其他接口写法相似,只不过它的每一个可选属性在声明时,用一个”?”进行标注。
可选属性的优势在于你可以描述这些可能是可利用的属性,然而也会捕获可能不可用的属性。
接口可以描述广泛的JavaScript对象可以构建的模型,除了描述具有属性的对象之外,接口还可以描述函数类型。
为了描述具有接口的方法类型,我们给该接口一个调用签名。这就像一个只有参数列表和给定返回类型的方法声明。
1 //函数类型接口的声明 2 interface SearchFunc { 3 (source:string,substring:string):boolean; 4 } 5 6 var mySerach: SearchFunc; 7 mySerach = function (source: string, substring: string) { 8 var result = source.search(substring); 9 if (result==-1) {10 return false;11 } else {12 return true;13 }14 };
一旦定义了函数类型接口,就可以像其他接口一样使用,这里,我们演示了如何创建函数类型的变量以及给它赋一个相同类型的函数值。
对于函数类型的正确类型检查,不需要匹配参数的名字,比如上面例子的“source”命名为“src”。
方法参数每次只检查一次,相互检查每一个相应的参数的类型。上面的例子,我们的函数表达式返回的值(true和false)暗示了它的返回类型。如果上面返回的是number或string类型的值,类型检查器会警告我们“返回的类型不匹配在SearchFunc接口中描述的返回类型”。
类类型(Class Types)
实现一个接口
在像C#和Java语言中,接口最通常的用法之一是,显式地强一个类满足一个特定的契约,这在TypeScript中也是可以的。
1 interface ClockInterface { 2 currentTime: Date; 3 setTime(d:Date); 4 } 5 class Clock implements ClockInterface { 6 currentTime: Date; 7 constructor(h: number, m: number) { }; 8 setTime(d: Date) { 9 this.currentTime = d;10 }11 }
接口描述了类的公共的部分,而不是公共和私有两部分。这会阻止你使用它们来检查一个类的实例的私有部分也有特定的类型。
类的静态/实例端之间的区别
当使用类和接口时,它帮助记忆类有两种类型:静态类型和实例类型。你可能会注意到,如果你使用构造签名创建了一个接口,并且尝试创建一个实现了该接口的类时,你会得到一个错误:
1 /***************错误的写法***************/ 2 interface ClockInterface { 3 new (hour: number, minute: number); 4 } 5 6 class Clock implements ClockInterface { 7 currentTime: Date; 8 constructor(h: number, m: number) { } 9 }10 /**************错误的写法****************/11 /***************正确的写法***************/12 interface ClockStatic {13 new (hour: number, minute: number);14 }15 16 class Clock2{17 currentTime: Date;18 constructor(h: number, m: number) { }19 }20 21 var cs: ClockStatic = Clock;22 var newClock = new cs(7, 30);23 /**************正确的写法****************/
这是因为,当一个类实现一个接口时,只会检测类的实例端,因为constructor位于静态端,所以会被检测到。相反,你可能需要直接使用类的“静态”端。
继承接口(Extends Interfaces)
像类一样,接口也可以互相继承。它处理复制一个接口的成员到另一个接口的任务,在如何分离接口为可重用的组件时给你更多的自由。
1 interface Shape { 2 color: string; 3 } 4 5 interface Square extends Shape { 6 sideLength: number; 7 } 8 9 var square = <Square>{};10 square.color = "blue";11 square.sideLength = 10;
一个接口可以继承多个接口,这样就创建了所有接口的组合。
1 interface Shape { 2 color: string; 3 } 4 5 interface PenStroke { 6 penWidth: number; 7 } 8 9 interface Square extends Shape, PenStroke {10 sideLength: number;11 }12 13 var square = <Square>{};14 square.color = "blue";15 square.sideLength = 10;16 square.penWidth = 5.0;
杂交类型(Hybrid Types)
1 interface Counter { 2 (start: number): string; 3 interval: number; 4 reset(): void; 5 } 6 7 var c: Counter; 8 c(10); 9 c.reset();10 c.interval = 5.0;
当和第三方JavaScript交互时,你可能需要使用像上面的模式来完全描述该类型的模型。
TypeScript入门之接口
在TypeScript里,接口的作用就是类型命名和为你的代码或第三方代码定义契约。在接口中我们只需要定义属性或方法,不需要具体的实现。一般我们用接口来定义对象类型和方法类型。