接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。
在typescript中接口可以这样理解,只定义属性的类型或者方法的参数类型和返回值类型,至于具体的值是什么,接口不关心,只需要类型保持一致,符合接口的类型约定。
接口定义
使用关键字interface
interface interface_name {
}
interface Fb {
foo:string,
bar:string,
sayHi: function():string {
return 'hello typescript'
}
}
var customer:Fb = {
firstName:"Tom",
lastName:"Hanks",
sayHi: ():string =>{return "Hi there"}
}
可选属性
定义可选属性使用?:
,接口中只要定义了可选属性,就可以认为使用了“option bags”模式
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"});
只读属性
定义只读属性使用readonly
interface Point {
readonly x: number;
readonly y: number;
}
只读属性在实现接口后(初始化后)不能对属性进行重新赋值
额外的属性检查
当使用option bags模式式,需要注意额外的属性检查。
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
// ...
}
let mySquare = createSquare({ colour: "red", width: 100 }); //error: 'colour' not expected in type 'SquareConfig'
解决方式
- 类型断言
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig); let mySquare = createSquare(<SquareConfig>{ width: 100, opacity: 0.5 } );
函数类型
interface SearchFunc { //抽象声明
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) { // 具体实现
let result = source.search(subString);
return result > -1;
}
索引类型
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
:::warning
TypeScript支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number来索引时,JavaScript会将它转换成string然后再去索引对象。
也就是说用 100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致
:::
比如
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
// 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
interface NotOkay {
[x: number]: Animal; // error 数字索引类型“Animal”不能赋给字符串索引类型“Dog”
[x: string]: Dog;
}
// 正确
interface NotOkay {
[x: number]: Dog;
[x: string]: Animal;
}
类类型
实现接口
implements关键字创建一个接口类
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface { //Clock是一个接口类,需要实现ClockInterface接口
currentTime: Date;
constructor(h: number, m: number) { }
}
在接口中描述一个方法
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
constructor属于类的静态部分,typescript不会对静态部分进行检查
继承接口
使用关键字extends
单个继承
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
多继承
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
混合类型
一个接口同时作为对象和函数使用
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
接口继承类
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() { }
}
class TextBox extends Control {
select() { }
}
// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl { //Image不是Control的子类,所以无法实现SelectableControl
select() { }
}
class Location {
}
在上面的例子里,SelectableControl包含了Control的所有成员,包括私有成员state。
因为 state是私有成员,所以只能够是Control的子类们才能实现SelectableControl接口。
因为只有 Control的子类才能够拥有一个声明于Control的私有成员state,这对私有成员的兼容性是必需的。
在Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。 实际上,
SelectableControl接口和拥有select方法的Control类是一样的。 Button和TextBox类是SelectableControl的子类
(因为它们都继承自Control并有select方法),但Image和Location类并不是这样的。