接口
接口概述
function printLabel(labeledObj: { label: string }) {
console.log(labeledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
// 定义接口
interface LabeledValue{
label:string;
}
// 接口限定函数的参数类型
function printLabel(labeledObj:LabeledValue){
console.log(labelObj.label)
}
let myObj = {size:10,label:'Size 10 Object'}
printLabel(myObj)
-
编译器只会检查那些必需的属性是否存在,并且其类型是否匹配
-
不能说传给
printLabel的对象实现了接口;我们只关系值的外形,只要传入的对象满足参数个数和参数类型兼容的条件,那么就是被允许的。 -
类型检查器不会检查属性的顺序,只要相应的属性存在且类型也是对的就可以
可选属性
带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ? 符号
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.width = config.width;
}
return newSquare;
}
let mySquare = create({color:'red'})
可选属性有以特点
- 对可能存在的属性进行预定义
- 是可以捕获引用了不存在的属性时的错误。
只读属性
对象的属性只能在对象刚刚创建的时候修改其值。可以在属性名前加readonly来指定只读属性。
interface Point{
// 通过使用readonly修饰的属性为只读属性
readonly x:number;
readonly y:nubmer;
}
let p1:Point = {x:15,y:20}
p1.x=5;// error!
函数类型
使用接口描述函数类型
- 给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义
- 参数列表里的每个参数都需要名字和类型
- 函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。
- 如果不写函数形参的类型,Typescript会自动推断参数的类型
interface SearchFunc{
// 调用签名
(source:string,subString:string):boolean;
}
// 将同类型的函数赋值给这个变量
let mySearch:SearchFunc = (source:string,subString:string){
let result = source.search(subString);
return result > -1;
}
// 函数的形参名不需要和定义的接口名相同,但是对应位置上的参数类型必须兼容
let my_search:SearchFunc =(src:string,sub:string)=>{
let result = src.search(sub)
return result > -1
}
可索引的类型
- 可索引类型具有一个索引签名,它描述了对象索引的类型, 还有相应的索引返回值类型
TypeScript支持两种索引签名:字符串和数字;可以同时使用这两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型
interface StringArray{
// 索引签名(当使用number去索引StringArray是会得到String类型的返回值)
[index:number]:string;
}
let myArray:StringArray = ['Bob','Fred']
let myStr:string = myArray[0]
class Animal{
name:string
}
class Dog extends Animal{
breed:string;
}
/**
* 内部会将number类型转换为string类型在查找,
* 所以number索引的返回值类型必须是string索引返回值类型的子类型
*/
interface NotOkay{
// 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
[x:number]:Animal;
[x:string]:Dog;
}
类类型
TypeScript 也能够用接口来明确的强制一个类去符合某种契约
- 强制类有某个属性
- 在接口中描述一个方法,在类里实现它
- 接口只描述类公共部分,并不会检查类的私有部分。
interface ClockInterface{
// 强制类必须有currentTime属性
currentTime:Date;
// 在接口中描述方法,在勒种实现方法
setTime(d:Date):void;
}
class Clock implements ClockInterface{
constructor(h:number,m:number){
}
currentTime: Date = new Date();
setTime(d:Date){
this.currentTime = d;
}
}
类静态部分与 实例部分的区别
// 用构造器签名去定义一个接口并试图定义一个类去实现这个接口
interface ClockConstructor{
new (hour:number,minute:number)
}
class Clock implements ClockContructor{
currentTime:Date;
constructor(h:number,m:number)
}
// error
// 因为当一个类实现了一个接口时,只对其实例部分进行类型检查。
// constructor 存在于类的静态部分,所以不在检查的范围内
// 为构造函数所用
interface ClockContructor {
new(hour: number, minute: number): ClockInterface
}
// 为实例方法所用
interface ClockInterface {
tick(): void
}
// 定义构造函数
function createClock(ctor: ClockContructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute)
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) {
}
tick() {
console.log('beep beep')
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log('tick tick')
}
}
// 使用构造函数创建实例digital
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
/**
* 因为 createClock 的第一个参数是 ClockConstructor 类型,
* 在 createClock(AnalogClock, 7, 32) 里,
* 会检查 AnalogClock 是否符合构造函数签名。
*/
接口的继承
- 接口也可以相互继承。 让我们能够从一个接口里复制成员到另一个接口里,可以更灵活 地将接口分割到可重用的模块里
- 一个接口可以继承多个接口,创建出多个接口的合成接口
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
/**
* 定义一个空对象使用类型断言将其转换为Square类型
* 子接口具有父接口的属性
* */
let square = {} as Square;
square.color = 'blue'
square.sideLength = 10;
混合类型
interface Counter {
// 函数签名
(start: number): string;
interval: number;
// 必须有一个reset方法
reset(): void
}
/**
* 构造函数
*/
function getCounter(): Counter {
// 将函数返回值强转成Couter类型
let counter = function (start: number) { } as Counter
counter.interval = 123
// 实例counter的reset方法
counter.reset = function () { }
return counter
}
let c = getCounter()
c(10)
c.reset()
c.interval = 5.0
接口继承类
当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的 private 和 protected 成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)
class Control {
private state: any
}
// 接口继承了类
/**
* SelectableControl 包含了 Control 的所有成员,包括私有成员 state 。
* 因为 state 是私有成员,所以只能够是 Control 的子类们才能实现 SelectableControl 接口。
* 因为只有Control 的子类才能够拥有一个声明于 Control 的私有成员 state ,这对私有成员的兼容性是必需的
*
* 在 Control 类内部,是允许通过 SelectableControl 的实例来访问私有成员 state 的。
* 实际上, SelectableControl 就像 Control 一样,并拥有一个 select 方法。
* Button 和 TextBox 类是 SelectableControl 的子类(因为它们都继承自 Control 并 有 select 方法)。
* 而对于 ImageControl 类,它有自身的私有成员 state 而不是通过继承 Control 得来的,所以它不可以实现 SelectableControl
*/
interface SelectableControl extends Control {
select(): void
}
// Button继承了Control类 实现了SelectableControl接口
class Button extends Control implements SelectableControl {
select() { }
}
// TextBox继承自Control类
class TextBox extends Control {
select() { }
}
/**
* Class 'ImageControl' incorrectly implements interface 'SelectableControl'.
* Types have separate declarations of a private property 'state'.
*
* 类“ImageControl”错误地实现了接口“SelectableControl”。
* 类型具有私有属性“state”的单独声明。
*/
class ImageControl implements SelectableControl {
private state: any;
select() { }
}
阿斯蒂芬
浙公网安备 33010602011771号