接口定义
接口和Java语言一样,都是通过关键字interface定义的,如下例子:
interface People {
name: string;
age: number;
hobby?: string; // 可选属性
readonly sex: string; // 只读属性
run: (steps: number) => void; // 箭头函数
play():void; // 普通函数
}
接口使用
function test(people:People) {
// content
people.sex = 'xxx'; // 编译器报错
}
let p = {
age: 1, name: 'z', sex: 'x',
run: (s: number) => {
return 1;
},
play:function() {
},
height:180
}
demo(p);
如上例子,当前对象p
作为参数传入函数test
中时,编译器只会检查那些必需的属性(函数也是一种属性)是否存在,类型是否匹配,如果缺少必要的属性,编译器会报错。至于多出的属性height
,编译器则不会检查,这点我们可以这样理解:对象p
是People
的实现,也就是其子类。height是子类中的属性,这样的话很容易理解。
可选属性
接口里的属性不全都是必需的。属性名后面加上?
表示是一个可选属性。
有些是只在某些条件下存在,或者根本不存在.如上述例子中hobby
属性,我们在构造p对象时就算没有传入hobby属性,编译器不会报错。
只读属性
只读属性用readonly
修饰,一旦该属性赋值后,就不能再修改了,否则编译器报错。
前几章中学习了常量const
,它们没有什么差别,最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用readonly。
函数
关于函数的声明有两种,箭头函数和普通方式。在上面的例子中,我们声明了两个函数run
和play
,虽然生命方法不一样,但是使用方法是一样的,只是作用域不一样,究竟有什么不一样,以后再讲。
接口实现和继承
类可以实现接口,接口也可以继承接口并且只能描述公有属性,这些都是和Java是一样的。
class Player implements People {
name: string;
age: number;
readonly sex: string; // 只读属性
constructor() {
}
run= (steps: number) => {
} // 箭头函数
play():number {
return 100;
}
}
interface Player extends People {
item:string;
}
接口继承类,多继承
在Java中接口只能继承接口,而且只能继承一个接口,但是ts中的接口可以继承类,同时支持多继承。接口继承只能继承类中的属性和函数(不继承其实现)。父类中的私有属性和受保护属性也能继承,但是如果这样的继承的话,这个接口类型只能被这个类或其子类所实现。
class Animal {
name: string;
private age: string;
run(): void {
console.log('running...');
}
}
interface Person extends Animal {}
function eat(p: Person) {}
let p = { name: '', age: 'x', sayHello: function () { return '' } };
eat(p); // 编译报错
上述代码会报错,因为age是私有属性,而对象p中age是公有的,这样会产生冲突。所以接口Person必须是Animal的子类所实现。
要想编译不报错,有两种方法:第一是将age属性改为公有,第二种是新增一个类,该类是Animal子类, 如下:
class Animal {
name: string;
private age: string;
run(): void {
console.log('running...');
}
}
interface Person extends Animal {
say(): void;
}
class Child extends Animal {
say(): void {
}
}
function eat(p: Person) { }
let p = new Child();
eat(p);// 这样不会报错