TypeScript 中接口详解


Posted in Javascript onJune 19, 2015

在 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?: string;
  width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
  var newSquare = { 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;

这样就使用到三种类型了,分别是方法(接口自己是个方法)、属性、方法(定义了方法成员)。

以上所述就是本文的全部内容了,希望大家能够喜欢。

Javascript 相关文章推荐
Javascript 构造函数 实例分析
Nov 26 Javascript
Javascript 面向对象之重载
May 04 Javascript
用jquery修复在iframe下的页面锚点失效问题
Aug 22 Javascript
JavaScript字符串对象replace方法实例(用于字符串替换或正则替换)
Oct 16 Javascript
JavaScript插件化开发教程(五)
Feb 01 Javascript
基于jQuery.validate及Bootstrap的tooltip开发气泡样式的表单校验组件思路详解
Jul 18 Javascript
Bootstrap表格制作代码
Mar 17 Javascript
JS实现动态添加DOM节点和事件的方法示例
Apr 28 Javascript
JS写XSS cookie stealer来窃取密码的步骤详解
Nov 20 Javascript
vue.js响应式原理解析与实现
Jun 22 Javascript
jQuery 操作 HTML 元素和属性的方法
Nov 12 jQuery
vue配置nprogress实现页面顶部进度条
Sep 21 Javascript
TypeScript 学习笔记之基本类型
Jun 19 #Javascript
使用Chrome浏览器调试AngularJS应用的方法
Jun 18 #Javascript
使用AngularJS创建自定义的过滤器的方法
Jun 18 #Javascript
深入讲解AngularJS中的自定义指令的使用
Jun 18 #Javascript
3个可以改善用户体验的AngularJS指令介绍
Jun 18 #Javascript
在AngularJS应用中实现一些动画效果的代码
Jun 18 #Javascript
使用AngularJS对路由进行安全性处理的方法
Jun 18 #Javascript
You might like
PHP也能干大事 随机函数
2015/04/14 PHP
Yii2配置Nginx伪静态的方法
2017/05/05 PHP
Laravel框架中Blade模板的用法示例
2017/08/30 PHP
PHP迭代器接口Iterator用法分析
2017/12/28 PHP
Jquery ajax不能解析json对象,报Invalid JSON错误的原因和解决方法
2010/03/27 Javascript
用js判断页面刷新或关闭的方法(onbeforeunload与onunload事件)
2012/06/22 Javascript
Three.js源码阅读笔记(基础的核心Core对象)
2012/12/27 Javascript
Javascript中克隆一个数组的实现代码
2013/12/06 Javascript
jquery 图片缩放拖动的简单实例
2014/01/08 Javascript
javascript正则匹配汉字、数字、字母、下划线
2014/04/10 Javascript
Javascript中设置默认参数值示例
2014/09/11 Javascript
PHP PDO操作总结
2014/11/17 Javascript
Angularjs编写KindEditor,UEidtor,jQuery指令
2015/01/28 Javascript
jQuery实现的五子棋游戏实例
2015/06/13 Javascript
JavaScript 浏览器兼容性总结及常用浏览器兼容性分析
2016/03/30 Javascript
常用原生JS兼容性写法汇总
2016/04/27 Javascript
微信小程序实现横向增长表格的方法
2018/07/24 Javascript
vue下使用nginx刷新页面404的问题解决
2019/08/02 Javascript
Vue2.0 $set()的正确使用详解
2020/07/28 Javascript
JavaScript 判断浏览器是否是IE
2021/02/19 Javascript
[36:20]KG vs SECRET 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
Python中单、双下划线的区别总结
2017/12/01 Python
python实现多线程网页下载器
2018/04/15 Python
使用celery和Django处理异步任务的流程分析
2020/02/19 Python
Python猜数字算法题详解
2020/03/01 Python
基于python实现百度语音识别和图灵对话
2020/11/02 Python
英国在线自行车商店:Evans Cycles
2016/09/26 全球购物
Needle & Thread官网:英国仙女品牌
2018/01/13 全球购物
什么是lambda函数
2013/09/17 面试题
Java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
2012/05/30 面试题
行政助理的职责
2013/11/14 职场文书
课程设计心得体会
2013/12/28 职场文书
在职员工证明书
2014/09/19 职场文书
Python+uiautomator2实现自动刷抖音视频功能
2021/04/29 Python
自己搭建resnet18网络并加载torchvision自带权重的操作
2021/05/13 Python
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
2021/06/09 Vue.js