您现在的位置: 万盛学电脑网 >> 程序编程 >> 脚本专题 >> javascript >> 正文

TypeScript 中接口详解

作者:佚名    责任编辑:admin    更新时间:2022-06-22

   TypeScript核心设计原则之一就是类型检查,通过使用接口(Interfaces)可以进行类型检查,满足传统面向对象思想,利于有效开发,有效避免类型转换问题。

  在 TypeScript 中,接口是用作约束作用的,在编译成 JavaScript 的时候,所有的接口都会被擦除掉,因为 JavaScript 中并没有接口这一概念。

  先看看一个简单的例子:

  ?

1 2 3 4 5 6 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)来定义这个方法的参数类型。

  ?

1 2 3 4 5 6 7 8 9 10 interface LabelledValue { label: string; }   function printLabel(labelledObj: LabelledValue) { console.log(labelledObj.label); }   var myObj = { size: 10, label: "Size 10 Object" }; printLabel(myObj);

  可选属性

  有些时候,我们并不需要属性一定存在,就可以使用可选属性这一特性来定义。

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 中,我们可以使用接口来约束方法的签名。

  ?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 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; } }

  上面代码中,我们定义了一个接口,接口内约束了一个方法的签名,这个方法有两个字符串参数,返回布尔值。在第二段代码中我们声明了这个接口的实现。

  需要注意的是,编译器仅仅检查类型是否正确(参数类型、返回值类型),因此参数的名字我们可以换成别的。

  ?

1 2 3 4 5 6 7 8 9 10 var mySearch: SearchFunc; mySearch = function(src: string, sub: string) { var result = src.search(sub); if (result == -1) { return false; } else { return true; } }

  这样也是能够编译通过的。

  数组类型

  在上面我们在接口中定义了方法类型,那么,数组类型又应该如何定义呢?很简单。

  ?

1 2 3 4 5 6 interface StringArray { [index: number]: string; }   var myArray: StringArray; myArray = ["Bob", "Fred"];

  那么 myArray 就是一个数组,并且索引器是 number 类型,元素是 string。

  在接口的定义里面,索引器的名字一般为 index(当然也可以改成别的,但一般情况下都是保持名字为 index)。所以改成

  ?

1 2 3 4 5 6 interface StringArray { [myIndex: number]: string; }   var myArray: StringArray; myArray = ["Bob", "Fred"];

  也是 ok 的。

  需要注意的是,索引器的类型只能为 number 或者 string。

  ?

1 2 3 4 5 6 7 interface Array{ [index: number]: any; }   interface Dictionary{ [index: string]: any; }

  上面两段都是可以编译通过的。

  最后还有一点要注意的是,如果接口已经是数组类型的话,接口中定义的其它属性的类型都必须是该数组的元素类型。例如:

  ?

1 2 3 4 interface Dictionary { [index: string]: string; length: number; // error, the type of 'length' is not a subtype of the indexer }

  那么将无法编译通过,需要将 length 改成 string 类型才可以。

  使用类实现接口

  一般情况下,我们还是习惯使用一个类,实现需要的接口,而不是像上面直接用接口。

  ?

1 2 3 4 5 6 7 8 interface ClockInterface { currentTime: Date; }   class Clo