接口

TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

Typescript的接口概念与java是有一定的差异的,typescript更趋向于一个约定,为我们的代码制定一个定向的规则,而java则更多的是为了继承和封装。

以下代码,printLabel要求我们必须传入一个labelledObj参数,并且是要带有一个变量类型为string的label属性。这是我们在没有接口之前将对象作为参数传递的一种方式。

function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

当我们使用了接口(interface)后,可以将代码改写为以下更具有通用性和封装性的写法。另外有一个小的细节点,传参的对象属性可以不用按接口属性的顺序一样。

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

可选属性
接口的属性可以被声明为可选属性,当属性被声明可选属性的时候,不传递该属性的时候也不会出现报错信息。

interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
  let newSquare = {color: "white", area: 100};
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({color: "black"});

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。

只读属性

通常我们定义变量的时候,碰到需要定义一些只读字段的时候,会使用const来声明该变量,而在接口中,我们可以使用readonly来代表该属性属于只读,并且以此限制编程规范。

interface Point {
    readonly x: number;
    readonly y: number;
}

普通属性的只读声明,我们可以用到以上的方式,但是对于数组我们可以使用如下方式。

TypeScript具有ReadonlyArray类型,它与Array相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改:

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

可扩展属性

可选属性对于我们编写接口的时候帮助很大,可以让我们预定义一个可能会存在的变量,但是它是已经被我们人为制定的一条规则,当我们可能需要一个变动可增的属性时,可选属性在大多时候并不能满足我们的需求,于是会便出现了另一种声明属性的方式。

interface SquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}

函数型接口

除了我们常见的对象接口,Typescript还提供了函数类型的接口让我们使用。

interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
  let result = source.search(subString);
  return result > -1;
}

对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。 比如,我们使用下面的代码重写上面的例子:

let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
  let result = src.search(sub);
  return result > -1;
}

可索引的类型

可索引类型其实就是类似JS中我们使用对象的属性或者数组的下标去获取对应的元素。

比如一下我们声明了一个index的索引,那么当我们用数字去访问我们数组的时候,就能获取到对应索引位置的元素值。

interface StringArray {
  [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

类类型
类类型就有点类似于我们JAVA和C#中常用到的手段,声明一个接口,代表了一个规范,然后可以通过implement去实现这个接口。

interface ClockInterface {
    currentTime: Date;
}

class Clock implements ClockInterface {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

结语

Typescript中的接口,其实是一种为了让javascript成为更接近java的一种手段,javascript的代码编写常常会因为编码规范的而引起各种代码错误,而Typescript则通过新增类似于接口这样的不同方式去限制并且提高代码的易阅读性,可扩展性,从而也避免了遇到在js中出现的多种未知的问题。