TypeScript接口
Posted DieHunter1024
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TypeScript接口相关的知识,希望对你有一定的参考价值。
目录
前言
在介绍TS对象类型中,为了让数组每一项更具体,我们使用 string [ ] 表示字符串类型的数组,为了知道函数的参数与返回值,使用 let fn: (a: number, b: number) => number 来表示一个函数类型,那么作为复杂类型,仅仅使用Object表示一个普通对象类型是远远不能满足类型检查以及代码可读性的,有没有一种类型可以用来描述对象的结构呢?
这便是今天的主题:接口
定义
接口(Interface)是一种定义对象形状的方式,它指定了对象具备或拥有哪些属性和方法,可以用来定义对象属性值和属性名的类型。使用接口来定义对象可以使代码更健壮,清晰。与Java的接口不同,TS接口除了能够描述类,还可以描述对象,函数等。
用法
基本用法
接口使用interface作为关键词,与JS中类(class)的写法相似,下面是一个JS类
class Animal
color = "black";
showColor = () => this.color
console.log(new Animal().showColor());
我们定义了一个Animal类,其中包含1个属性以及1个行为;那么在我们抽象构想这个类时可能只知道它的类型,比如:color可能是字符串类型,showColor函数返回一个颜色字符串;
接口的写法如下
interface 接口名称
属性名: 属性类型
函数名(参数类型列表): 返回值类型
让我们稍作改动,用接口的方式实现这个类
interface Animal
color: string
showColor: () => string
怎么样?是不是觉得接口不算太难,只需要仿照class的写法,将类抽象成类的形状(属性的类型),就可以实现一个接口
参照之前基本类型的写法,我们新增一个对象,使用对象实现这个抽象的接口
interface Animal
color: string
showColor: () => string
const animal: Animal =
color: "blue",
showColor()
return this.color
,
console.log(animal.showColor());
约定规则
一般我们定义接口时,命名规则是在名称前加 I ,即上述接口名是:IAnimal
在使用接口定义对象时会遇到属性不匹配的情况,比如上述代码我们改成
const animal: Animal =
color: "black",
name: "dog",
showColor()
return this.color
,
此时编辑器会提示 name 不在类型 Animal 中
那么我们去掉name和color,只保留showColor函数呢?
const animal: Animal =
showColor()
return this.color
,
编辑器会提示:类型中缺少属性 color。
或者我们不想修改animal中的color属性,让它始终是black
那么有没有办法使接口支持上述属性的操作呢?请接着往下看
属性控制
在接口中,每个属性都有3种选项,分别是可选,只读,任意;换句话说,接口中的属性可以设置成可变的。
任意属性
在接口定义时,在属性名后面加上索引签名来表示接口可以有任意数量的属性。如:
interface Animal
color: string
showColor: () => string
[key: string]: unknown
这个接口可以匹配约定规则中的第一段代码;
tips:索引签名参数(上述代码的key)类型支持number,string,symbol,模板字符;示例如下
interface Animal
[key: symbol | string | number]: unknown
const str = "name"
const animal: Animal =
0: "dog",
[str]: "dog",
[Symbol("name")]: "dog",
可选属性
在定义接口时,我们可能无法判断某个属性是否存在。此时一个可选的属性操作可以为我们解决此问题,我们在属性名后面增加一个 ? 问号用于为属性增加可选操作,如:
interface Animal
color?: string
const animal: Animal =
color: "black"
const animal2: Animal =
此时color属性在对象中便可有可无,这个接口可以适配约定规则中的第二段代码。
只读属性
顾名思义,只读属性保证了对象中某个属性只允许读取,不允许修改
interface Animal
readonly color: string
const animal: Animal =
color: "black"
animal.color = "white"
上述代码中会在编译前报错:无法分配到 color ,因为它是只读属性
在JS中我们同样可以控制对象中属性的只读,即只设置属性get而不使用set操作,代码如下
const animal =
_color: "black",
get color()
return this._color
animal.color = "white"
定义函数
函数在接口中有两种表现形式,分别是冒号定义和箭头定义
冒号定义
interface 接口名
函数名 (参数类型) : 函数返回类型
箭头定义
interface 接口名
函数名: (参数类型) => 函数返回类型
接口类型
接口除了上述展示的对象接口外,还有函数接口,索引接口,继承接口,类接口,下面我会一一列举。
函数接口
在对象类型中,我们说到了函数类型的定义的方式有两种分别是Function关键词和 ( )=>void 箭头函数,那么在本文,我们会接触到第三种定义函数的方式,接口
interface IFn
(): void
我们通过上述代码实现一个无返回值的函数接口,冒号(:)前面的括号表示参数,后面表示函数返回值,结合之前的知识,我们写一个加法函数
interface IFn
(a: number, b: number): number
const add: IFn = (a, b) =>
return a + b
索引接口
同样在对象类型文章中,我们提到了使用接口定义数组类型
interface IArray
[i: number]: any
通过定义索引值 i 的类型为 number 来描述一个数组类型
interface IArray
[i: number]: string
const list: IArray = ['a', 'b', 'c']
继承接口
和JS中的类一样,接口类型也可以继承操作,被继承的接口拥有父接口的属性及方法
interface IAnimel
name: string
interface IDog extends IAnimel
likeMeat: boolean
interface IWhiteDog extends IDog
color: string
const whiteDog: IWhiteDog =
name: "阿黄",
likeMeat: true,
color: "white"
上述代码实现了一个连续的接口继承,子类IWhiteDog拥有父类的属性。
继承接口与继承类不同,接口可以通过多继承实现,上述代码可以修改为以下代码
interface IAnimel
name: string
interface IDog
likeMeat: boolean
interface IWhiteDog extends IAnimel, IDog
color: string
const whiteDog: IWhiteDog =
name: "阿黄",
likeMeat: true,
color: "white"
需要注意的是,执行多继承时,父接口的属性值可以重复,但类型必须相同
interface IAnimel
name: string
likeMeat: string
interface IDog
likeMeat: boolean
interface IWhiteDog extends IAnimel, IDog
color: string
上述代码会抛错:IAnimel 和 IDog 类型的命名属性 likeMeat 不完全相同
除了上面的继承接口外,TS还有一类继承,那便是接口继承类;TS与其他面向对象语言不同,它支持接口继承类中的属性类型及函数类型
将前面的代码修改一下,便可以达到和上面的代码一样的效果,实现接口对类的继承
class IAnimel
name = "阿黄"
class IDog
likeMeat = true
interface IWhiteDog extends IAnimel, IDog
color: string
const whiteDog: IWhiteDog =
name: "阿黄",
likeMeat: true,
color: "white"
需要注意的是:接口继承的是类的接口(可以理解为声明类的同时会创建类实例的接口类型,这个接口类型被当做是类的接口),所以接口继承的类实际上是类实例的接口
我们使用以下代码可以证实上面的说法
class IDog
static _likeMeat = true
likeMeat = false
interface IWhiteDog extends IDog
color: string
const whiteDog: IWhiteDog =
likeMeat: true,
_likeMeat: true,
color: "white"
上述代码的抛错:
说明接口可以继承类实例的属性,却不可以继承类中的静态属性
类接口
接口是一种抽象的类型,它的作用是描述对象的形状,增强可读性和可维护性。在接口与类之间,TS提供了一个 实现 (implements)关键词区别于传统的冒号(:)赋予类型,下面是一个类接口的例子
interface IAnimel
name: string
readonly color: string
getColor: () => string
getName?(): string
class Animal implements IAnimel
name = "dog"
color = "black"
getColor = () => this.color
看到这里不知道你是否会有疑问:接口可以描述类中的属性,那是否可以对构造函数进行描述?
答案是可以,但是和常规写法稍有不同。我们在接口中使用new表示类中的constructor
interface IAnimel
name: string
new(name: string): Animal
然而上面接口的写法无法使用类来实现
interface IAnimel
name: string
new(name: string): Animal
class Animal implements IAnimel
name: string;
constructor(name)
this.name = name
为什么会抛错呢?
参考之前的一篇文章:JS继承,因为在TS的类型中,类的构造函数和实例是两个不同的类型:构造函数是一个特殊的函数,它在创建类的实例时被调用,并返回一个该类的实例;类的实例则包含了类的所有属性和方法
有没有使我们实现接口的同时对构造函数进行描述的方法呢?
且看下面的代码
interface IAnimel // 描述实例
name: string
interface IAnimelConstructor // 描述构造函数
new(name: string): Animal
class Animal implements IAnimel // 实现接口
name: string;
constructor(name)
this.name = name
const createAnimal = (__Animal: IAnimelConstructor): IAnimel => // 工厂模式解决接口的局限性
return new __Animal('dog')
const animal = createAnimal(Animal)
console.log(animal);
代码中我使用两个接口来描述一个类的构造函数及实例,使用工厂模式解决接口的局限
总结
本文讲述了TypeScript中的接口类型,从定义,意义,用法,属性特性,继承,接口实现及局限性这几个方面详细的介绍了接口,通过代码案例了解其具体用法
感谢你看到了这里,如果文章对你有帮助,希望支持一下博主,谢谢。
参考文章
Introduction - TypeScript 精通指南
TypeScript入门五:TypeScript的接口
- TypeScript接口的基本使用
- TypeScript函数类型接口
- TypeScript可索引类型接口
- TypeScript类类型接口
- TypeScript接口与继承
一、TypeScript接口的基本使用
1.1定义TypeScript接口的指令(interface)
接口让我们想到的第一个短语就是(API)。比如日常我们所说的调用某某程序的API一般都是跨应用的接口,如别的网站的或者APP的接口;还有我们每天都是用的编程语言的指令和内置的方法及属性,这些可以叫做编程语言的接口;还有令我们既爱又恨的各种平台、框架、库的庞大接口集,特别是这些东西每一次升级都会产生新的接口或者修改以前的接口,这些接口需要我们付出大量的学习时间。
而学习接口第一要务就是学习接口的语法,这些语法往往定义了各种读写规则,这里抛开接口背后实现的意义不谈,因为学习使用TypeScript接口主要就是学会如何自定义各种接口的规则,至于你将来要在程序中定义什么样的接口、根据什么定义接口规则都是要根据程序本身的性质决定的。
比如按照我们JS的形式给一个函数定义参数接口:
1 function printLabel(labelledObj: { label: string }) { 2 console.log(labelledObj.label); 3 } 4 let myObj = { size: 10, label: "Size 10 Object" }; 5 printLabel(myObj);
这里会有类型检查帮助我们检查传入(printLabel)函数的参数是否有指定的属性(label)。然后,来看看TypeScript接口如何定义上面这个参数接口:
interface LabelledValue { label: string; } function printLabel(labelledObj: LabelledValue) {//这里指定参数要符合LabelledValue接口规则,参数不许包含一个label属性,且类型为string console.log(labelledObj.label); } let myObj = {size: 10, label: "Size 10 Object"}; printLabel(myObj);
感觉这两种实现方式没有什么区别,但是对于严格TypeScript的接口来说就没有这门宽松了,TypeScript包含非常丰富的接口规则,定义TypeScript接口最基本的规则就有:可选属性、只读属性、额外属性检查。
1.2可选属性(option bags模式):在可选属性名称定义的后面加上(?)问号。注意使用可选属性后,就不能出现额外的属性了,除非使用后面的额外属性检查规则实现。
1 interface SquareConfig { 2 color?: string;//定义为接口的额外属性 3 width?: number;//定义为接口的额外属性 4 } 5 function createSquare(config: SquareConfig): {color: string; area: number} { 6 let newSquare = {color: "white", area: 100}; 7 if (config.color) { 8 newSquare.color = config.color; 9 } 10 if (config.width) { 11 newSquare.area = config.width * config.width; 12 } 13 return newSquare; 14 } 15 let mySquare = createSquare({color: "black"} );//可选择传入部分可选属性 16 let mySquare1 = createSquare({color: "black",width:120} ); 17 // let mySquare2 = createSquare({color: "black",widtg:120} );//不能出现属性名不一致,这同样被识别为额外属性,出现2345错误 18 // let mySquare3 = createSquare({color: "black",width:120,size:120} );//这里出现了一个额外属性size,会出现2345错误
1.3只读属性:实现只读属性接口时不能不写只读属性,并且也不能出现额外属性,除非使用额外属性检查。
readonly(指定属性只读):在接口类型中指定的属性前面添加readonly修饰符来指定该属性为指定。
ReadonlyArray<T>(定义只读数组):定义只读数组类型结构的接口直接使用ReadonlyArray<T>类型来定义就可以了,不需要使用interface关键字。在官方文档中提到了ReadonlyArray<T>的底层实现就是将数组的可变方法去掉了。
1 //readonly 2 interface Point { 3 readonly x: number; 4 readonly y: number; 5 } 6 function fun(p:Point):number{ 7 return p.x + p.y; 8 } 9 fun({x:1,y:2}) 10 // fun({x:1})//只读属性必须全部写入参数 11 // fun({x:1,y:2,z:3})//不能添加额外的属性 12 fun({x:1,y:2,z:3} as Point)//这里使用了额外属性检查就不会报错 13 14 //ReadonlyArray<T>定义只读数组 15 let a:number[] = [1,2,3]; 16 let ro:ReadonlyArray<number> = a; 17 let ro1:ReadonlyArray<number> = [1,2,3,4]; 18 console.log(ro == ro1); 19 ro1 = a; 20 // a = ro; //number[] 类型不等于ReadonlyArray<T> ,这是变量类型那不内容的延伸了,TypeScript对变量的类型有严格的要求 21 // a = ro1;//同上 22 a = ro as number[];//可以使用断言重写ro来实现类型转换 23 // ro[1] = 100;//只读数组不能重写元素值
1.4额外属性检查:在前面的可选属性和只读属性都提到了额外属性检查,额外属性检查顾名思义就是接口中出现了额外的属性,而当在接口中定义可选和只读规则以后就不能出现额外属性,但是有时候在一些实现中可能出现额外属性就可以使用额外属性检查来实现。
额外属性检查就是在实现接口时使用“as”来告诉编译环境,这里允许出现额外属性,只要除额外属性以外的其他属性符合接口的规则就允许实现。
1 interface SquareConfig { 2 color?: string; 3 width?: number; 4 } 5 function createSquare(config: SquareConfig): { color: string; area: number } { 6 // ... 7 } 8 let mySquare = createSquare({ colour: "red", width: 100 });//这里不小心把color写成了colour,colour被识别为额外属性了 9 let mySquare1 = createSquare({ colour: "red", width: 100 } as SquareConfig);//使用额外属性检查就可以解决这类报错
关于额外属性检查,看是可以用来解决一些问题,实际上也确实可以解决一些问题,但是就上面的例子来说,这并不是一个好的结局方案,因为我们期望的时传入color的属性值,但是由于编码失误写错了单词,额外属性帮我们静默了这个错误,也同时让我们的程序偏离了我们期望的方向。关于这样的问题后面有一种索引签名的方式来解决,也就是后面的可索引的类型。
二、TypeScript函数类型接口
在TypeScript函数部分介绍了使用type定义Ts函数类型(详细可以了解官方文档或者这篇博客:TypeScript入门三:TypeScript函数类型),TypeScript接口也可以定义函数的接口类型,定义模式与type定义函数类型非常类似。
1 type myAdd = (baseValue: number, increment: number) => number; 2 interface inAdd { 3 (baseValue: number, increment: number):number; 4 } 5 //根据函数类型和接口声明函数变量 6 let myAdd1:myAdd = function(x:number,y:number) : number{ 7 return x + y; 8 } 9 let myAdd2:inAdd = function(x:number,y:number) : number{ 10 return x - y; 11 }
函数实现函数接口和函数类的实例化都一样,其都属于函数内部语法,比如可以设置参数默认值;参数名称与函数类或者接口定义的可以不一致,只要参数对应的位置实现的参数类型一致就可以了。
三、TypeScript可索引类型接口
在前面的额外属性检查中就提到了可索引类型接口,并有说过可以通过可索引类型接口解决由于书写错误,并由额外属性导致的错误静默问题。先来了解可索引接口规则是什么?再看看如何解决前面的问题。
3.1数字索引的可索引类型接口:数字可索引类型其实本质上就是定义个数组类型,只要赋值符合该接口的定义就可以直接赋值。
1 interface StringArray { 2 [index: number]: string; 3 } 4 5 let myArray: StringArray; 6 myArray = ["Bob", "Fred"]; 7 let a = ["1","2","3"]; 8 myArray = a;//这里并不会失败,数字可索引类型其实本质上就是定义个数组类型,只要赋值符合该接口的定义就可以直接赋值 9 let myStr: string = myArray[0];
3.2字符串可索引类型接口:字符串可索引类型其本质就是定义对象类型,只要赋值符合该接口定义就可以直接赋值。
1 interface StringObject { 2 [key:string]:string 3 } 4 let myObj : StringObject = { 5 ‘name‘:‘他乡踏雪‘, 6 ‘professional‘:‘Web front end‘ 7 }
3.3字符串和数字两种索引签名组合使用:
这种复杂的接口应用在官方文档中只给出了一个引导性的示例,官方使用了文字描述使用方法,而示例只给出了错误使用提示,并没有给完整的正确示例,估计这让很多初学者比较头疼,我这两天到各个教程中寻找完整的正确示例,但都没有找到,最后通过n次测试终于实现了两种索引组合使用的正确示例。直接上代码,在代码的注释中解析这种接口实现的规则:
1 class Animal { 2 //定义类作为接口索引指向的值时: 3 // 类内部的成员必须初始化赋值或者基于构造函数实例化赋值 4 // 这里我采用了构造函数实例化赋值 5 name: string; 6 constructor(nameStr:string){ 7 this.name = nameStr; 8 } 9 } 10 class Dog extends Animal { 11 breed: string; 12 constructor(breedStr:string,nameStr:string){ 13 super(nameStr); 14 this.breed = breedStr; 15 } 16 } 17 // 数字和字符串索引签名组合使用时: 18 // 数字索引赋值类型必须是字符串索引赋值类型的子类型 19 // 这个示例中数字索引赋值的Dog类型就是字符串索引赋值的Animal的子类型 20 21 //接口中定义索引时索引签名的类型是关键, 22 // 索引定义的标识名称(示例中的‘x’)并不重要, 23 // 可以使用任何合法字符,我没有测试过特殊字符, 24 // 但实际上它是无意意义的,比如示例中两个x可以替换成x和y照样不影响接口实现 25 interface animals { 26 [x: number]: Dog; 27 [x: string]: Animal; 28 } 29 //基于数字与字符串索引组合的接口实现, 30 // 使用{}定义成对象类型 31 // 不能使用[]只实现数字索引的值 32 // 但可以使用{}只实现字符串索引的值 33 let myAnimals : animals = { 34 1:new Dog(‘Dog1‘,‘animal1‘), 35 ‘2‘:new Dog(‘Dog2‘,‘animal2‘), 36 ‘animal1‘:new Animal(‘animal1‘), 37 ‘animal2‘:new Animal(‘animal2‘) 38 } 39 // let myAnimals1 : animals = [new Dog(‘Dog1‘,‘animal1‘)];//报错 40 let myAnimals2 : animals = { //这里不报错 41 ‘animal1‘:new Animal(‘animal1‘), 42 ‘animal2‘:new Animal(‘animal2‘) 43 } 44 45 console.log(myAnimals);
在可索引类型类型接口中照样可以使用只读属性规则:
1 interface ReadonlyStringArray { 2 readonly [index: number]: string; 3 } 4 let myArray: ReadonlyStringArray = ["Alice", "Bob"]; 5 myArray[2] = "Mallory"; // error!
前面说要基于可索引类型接口展示一个解决额外属性产生的潜在问题,写到这里我感觉好像没有多大必要了,如果你真的搞懂了可索引类型接口规则,应该很轻松想到它可以如何实现,还有考虑到篇幅问题,就不在这里展示了,如果有需要可以在评论区留言。
四、TypeScript类类型接口
TypeScript类类型接口与抽象类非常类型,都是用来定义类的必须结构,但是类类型接口与抽象类还是有些区别,抽象类中可以实现具体的细节,但是在类类型接口中不能实现具体的细节。比如在抽象类中可以直接实现方法的具体内容,但是在类类型接口只能声明一个方法的类型(包括名称、参数类型、返回值类型),但不能在类类型接口中直接实现函数体的具体内容。
1 interface ClockInterface { 2 currentTime: Date;//指定强制实现的公共成员,在接口实现中必须初始化或者在构造其中赋值 3 setTime(d: Date):void;//指定强制实现的函数,在接口实现中必须实现,之一这里定义时需要有指定的返回值类型,void表示没有任何类型,也就是不返任何类型的回值 4 5 } 6 7 class Clock implements ClockInterface { 8 currentTime: Date; 9 //↑上面这里第一种是想方法:实现接口强制约定要实现的公共成员currentTime,在构造器中赋值实现 10 //↓ 下面这里第二种实现方法: 实现接口强制约定要实现的公共成员currentTime,初始化值的方式实现 11 // currentTime: Date = new Date(); 12 13 setTime(d: Date) {//函数默认不返回任何类型的值,默认值为undefined,但它不是undefined类型 14 this.currentTime = d; 15 } 16 constructor(h: number, m: number) { 17 this.currentTime = new Date(); 18 } 19 }
关于类类型接口与抽象类的区别还有:
增加其他内容与否:派生类实现抽象类时不能随意增加其他内容,一切根据抽象类定义的结构实现,但是在类类型接口的具体实现中可以增加类类型接口中没有定义的内容。
公共成员与私有成员:抽象类中可以实现公共成员,也可以实现私有成员。但是,类类型接口中非静态成员都是公共公共成员,而且并不需要public修饰,自动被默认为公共成员。
静态成员在抽象类和类类型接口中都可以直接使用static修饰符来定义。
上面都是关于修饰符的相关内容,还有一个问题就是类类接口中不能直接定义构造函数的相关构造结构,在官方文档中描述为类的静态部分,也就常说的constructor函数。而当对一个类类型接口做具体实现时,只会对类类型的实体部分进行检查,而不会对静态部分检查。
而且定义类类型接口时并不能直接使用constructor在类类型接口中定义相关结构,IDE会直接提示错误信息,官方给出的解决方案时使用构造器签名的方式来实现。具体的实现方案就是定义一个构造器签名的类类型接口、定义一个实体部分的类类型接口、然后在根据这两个类类接口的结构的具体实现写在一个类中,这个类的implements指向实体部分的类类型接口、最后定一个实例化工具方法,将方法的第一个参数类型设定为构造器签名的类类型接口类型,实现构造需要的参数跟着写后面,然后指定这个方法的返回类型为实体部分的类类型接口类型,方法内直接返回第一个参数的实例化,实例化时传入对应需要的实例化参数。
具体见下面这个官方文档中的示例,对照下面的注释应该很清晰了:
1 interface ClockConstructor {//定义构造器签名的类类型接口:作为类类型接口的构造函数的结构 2 new (hour: number, minute: number): ClockInterface;//构造函数的方法值设定为类类型主体类型 3 } 4 interface ClockInterface {//定义类类型接口的实体 5 tick():void; 6 } 7 //定义一个类类型接口实现的实例化工具方法,设置参数有:将类类型实现的类的类型设置为构造签名的类类型接口类型,以及构造器需要的参数 8 // 返回值类型设置为类类型接口实体类型 9 function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface { 10 return new ctor(hour, minute);//内部直接通过类类型的具体实现类实例化并返回即可 11 } 12 //类类型接口的具体实现,implements指向类类型接口的实体类型 13 class DigitalClock implements ClockInterface { 14 constructor(h: number, m: number) { }//内部构造函数根据定义的构造器签名的类类型接口结构实现 15 tick() { 16 console.log("beep beep"); 17 } 18 } 19 class AnalogClock implements ClockInterface { 20 constructor(h: number, m: number) { } 21 tick() { 22 console.log("tick tock"); 23 } 24 } 25 //将类类型接口的具体实现类作为参数以及构造函数需要的参数一并传入,实例化类类型接口具体实现的类 26 let digital = createClock(DigitalClock, 12, 17); 27 let analog = createClock(AnalogClock, 7, 32);
五、TypeScript接口与继承
接口继承与类继承没什么差异,通过继承可以将被继承的接口内容复制过来,通过继承可以更灵活的将接口分割重用,这里我就直接复制官方文档的一个示例代码展示了,因为从接口语法角度来说非常简单,但是如果要使用上面类类型接口那样的复杂接口来继承另当别论。
1 interface Shape { 2 color: string; 3 } 4 5 interface PenStroke { 6 penWidth: number; 7 } 8 9 interface Square extends Shape, PenStroke { 10 sideLength: number; 11 } 12 13 let square = <Square>{}; 14 square.color = "blue"; 15 square.sideLength = 10; 16 square.penWidth = 5.0;
接口实现混合类型(函数类型与函数对象的静态方法接口):
在前面已经介绍了关于函数类型接口的实现,但是仅仅只能实现一个单独的函数,在JS中函数本身就是一个对象,在函数对象上实现静态的工具方法是比较常见的现象,TypeScript定义函数类型接口并一同定义一些工具方法也可以实现,这有点像是继承加手动解构的模式,官方文档中的示例也是比较清晰易懂的,还是添加一些注释的方式解析吧。
1 interface Counter { 2 (start: number): string;//定义函数接口的主体部分 3 interval: number;//定义函数的静态属性接口 4 reset(): void;//定义函数的静态方法接口 5 } 6 7 function getCounter(): Counter {//这里可以说是继承,或许它更像时通过接口定义函数的类型(它真正的含义是标识这个方法返回的值是该接口类型) 8 let counter = <Counter>function (start: number) { };//通过<接口名称>获取函数主体部分,并赋给一个变量(实际上这个变量才是真正实现接口的函数类型对象) 9 counter.interval = 123;//直接在函数对象上实现接口中定义的静态属性 10 counter.reset = function () { };//直接在函数对象上实现接口中定义的静态方法 11 return counter;//返回这个被实现的函数 12 } 13 14 let c = getCounter(); 15 c(10); 16 c.reset(); 17 c.interval = 5.0;
接口继承类:
当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。
在实现这类接口的时候必须要同时实现继承该接口继承的类,或者继承该接口继承的类的子类,比如示例:
1 class Control { 2 private state: any; 3 } 4 5 interface SelectableControl extends Control { 6 select(): void; 7 } 8 //实现接口同时实现继承接口继承的类 9 class Button extends Control implements SelectableControl { 10 select() { } 11 } 12 13 class TextBox extends Control { 14 select() { } 15 } 16 //实现接口同时实现继承接口继承的类的子类 17 class TextBox1 extends TextBox implements SelectableControl{ 18 19 }
以上是关于TypeScript接口的主要内容,如果未能解决你的问题,请参考以下文章