在 TypeScript 中,接口是用作约束作用的,在编译成 JavaScript 的时候,所有的接口都会被擦除掉,因为 JavaScript 中并没有接口这一概念。
先看看一个简单的例子:
function printLabel(labelledObj: { label: string }) { console.log(labelledObj.label); } var myObj = { size: 10, label: "Size 10 Object" }; printLabel(myObj);
那么在该方法中,labelledObj 的类型就是 {label: string},看上去可能有点复杂,但我们看见看看下面 myObj 的声明就知道,这是声明了一个拥有 size 属性(值为 10)和 label 属性(值为 "Size 10 Object")的对象。所以方法参数 labelledObj 的类型是 {label: string} 即表明参数拥有一个 string 类型的 label 属性。
但是,这么写的话,这个方法看上去还是有点让人糊涂。那么就可以用接口(interface)来定义这个方法的参数类型。
interface LabelledValue { label: string; } function printLabel(labelledObj: LabelledValue) { console.log(labelledObj.label); } var myObj = { size: 10, label: "Size 10 Object" }; printLabel(myObj);
可选属性
有些时候,我们并不需要属性一定存在,就可以使用可选属性这一特性来定义。
interface SquareConfig { color"white", area: 100 }; if (config.color) { newSquare.color = config.color; } if (config.width) { newSquare.area = config.width * config.width; } return newSquare; } var mySquare = createSquare({ color: "black" });
那么我们就传入了实现一个 SquareConfig 接口的对象入 createSquare 方法。
既然完全是可有可无的,那么为什么还要定义呢?对比起完全不定义,定义可选属性有两个优点。1、如果存在属性,能约束类型,这是十分关键的;2、能得到语法智能提示,假如误将方法体中 color 写成 collor,那么编译是不通过的。
方法类型
在 JavaScript 中,方法 function 是一种基本类型。在面向对象思想中,接口的实现是靠类来完成的,而 function 作为一种类型,是不是能够实现接口呢?答案是肯定的。
在 TypeScript 中,我们可以使用接口来约束方法的签名。
interface SearchFunc { (source: string, subString: string): boolean; } var mySearch: SearchFunc; mySearch = function(source: string, subString: string) { var result = source.search(subString); if (result == -1) { return false; } else { return true; } }
上面代码中,我们定义了一个接口,接口内约束了一个方法的签名,这个方法有两个字符串参数,返回布尔值。在第二段代码中我们声明了这个接口的实现。
需要注意的是,编译器仅仅检查类型是否正确(参数类型、返回值类型),因此参数的名字我们可以换成别的。
var mySearch: SearchFunc; mySearch = function(src: string, sub: string) { var result = src.search(sub); if (result == -1) { return false; } else { return true; } }
这样也是能够编译通过的。
数组类型
在上面我们在接口中定义了方法类型,那么,数组类型又应该如何定义呢?很简单。
interface StringArray { [index: number]: string; } var myArray: StringArray; myArray = ["Bob", "Fred"];
那么 myArray 就是一个数组,并且索引器是 number 类型,元素是 string。
在接口的定义里面,索引器的名字一般为 index(当然也可以改成别的,但一般情况下都是保持名字为 index)。所以改成
interface StringArray { [myIndex: number]: string; } var myArray: StringArray; myArray = ["Bob", "Fred"];
也是 ok 的。
需要注意的是,索引器的类型只能为 number 或者 string。
interface Array{ [index: number]: any; } interface Dictionary{ [index: string]: any; }
上面两段都是可以编译通过的。
最后还有一点要注意的是,如果接口已经是数组类型的话,接口中定义的其它属性的类型都必须是该数组的元素类型。例如:
interface Dictionary { [index: string]: string; length: number; // error, the type of 'length' is not a subtype of the indexer }
那么将无法编译通过,需要将 length 改成 string 类型才可以。
使用类实现接口
一般情况下,我们还是习惯使用一个类,实现需要的接口,而不是像上面直接用接口。
interface ClockInterface { currentTime: Date; } class Clock implements ClockInterface { currentTime: Date; constructor(h: number, m: number) { } }
在 TypeScript 中,使用 class 关键字来声明了,这跟 EcmaScript 6 是一样的。
另外,我们可以使用接口来约束类中定义的方法。
interface ClockInterface { currentTime: Date; setTime(d: Date); } class Clock implements ClockInterface { currentTime: Date; setTime(d: Date) { this.currentTime = d; } constructor(h: number, m: number) { } }
在 TypeScript 中,我们可以为接口定义构造函数。
interface ClockInterface { new (hour: number, minute: number); }
接下来天真的我们可能会接着这么写:
interface ClockInterface { new (hour: number, minute: number); } class Clock implements ClockInterface { currentTime: Date; constructor(h: number, m: number) { } }
这是不行的!!!因为构造函数是 static(静态)的,而类仅能够实现接口中的 instance(实例)部分。
那么这个接口中定义的构造函数岂不是没作用?既然 TypeScript 提供了这项功能,那么肯定不会是没作用的。声明的方法比较特殊:
interface ClockStatic { new (hour: number, minute: number); } class Clock { currentTime: Date; constructor(h: number, m: number) { } } var cs: ClockStatic = Clock; var newClock = new cs(7, 30);
正常情况下我们是写 new Clock 的,这里就将 Clock 类指向了 ClockStatic 接口。需要注意的是,newClock 变量的类型是 any。
继承接口
像类一样,接口也能实现继承,使用的是 extends 关键字。
interface Shape { color: string; } interface Square extends Shape { sideLength: number; } var square = <Square>{}; square.color = "blue"; square.sideLength = 10;
当然也能继承多个接口。
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } var square = <Square>{}; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0;
需要注意的是,尽管支持继承多个接口,但是如果继承的接口中,定义的同名属性的类型不同的话,是不能编译通过的。
interface Shape { color: string; test: number; } interface PenStroke { penWidth: number; test: string; } interface Square extends Shape, PenStroke { sideLength: number; }
那么这段代码就无法编译通过了,因为 test 属性的类型无法确定。
同时使用上面所述的类型
如果仅能单一使用某种类型,那么这接口也未免太弱了。但幸运的是,我们的接口很强大。
interface Counter { (start: number): string; interval: number; reset(): void; } var c: Counter; c(10); c.reset(); c.interval = 5.0;
这样就使用到三种类型了,分别是方法(接口自己是个方法)、属性、方法(定义了方法成员)。
以上所述就是本文的全部内容了,希望大家能够喜欢。
TypeScript,接口
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。