持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情
接口
我们之前简单说了接口就是用来定义对象类型的,在对对象赋值的时候, 变量的形状必须和接口的形状保持一致
interface Person {
name: string;
age: number;
}
let p1: Person = {
name: 'Tom',
age: 25,
sex: 'male' //报错:不能将类型“{ name: string; age: number; sex: string; }”分配给类型“Person”。
};
------------------
interface Person {
name: string;
age: number;
}
let p1: Person = { //报错:类型 "{ name: string; }" 中缺少属性 "age",但类型 "Person" 中需要该属性
name: 'Tom'
};
可见多一些属性和少一些属性都是不行的。那么如果我们希望有些属性不是必要的咋办呢?这时候就需要使用可选属性了,和函数传参的可选参数是一样的。
interface Person {
name: string;
age?: number;
}
let p1: Person = {
name: 'Tom'
};
那么我们需要额外的属性又咋办呢?我们可以使用[]来定义任意属性。
interface Person {
name: string;
age?: number;
[propName:string]:any
}
let p1: Person = {
name: 'Tom',
sex:'male'
};
但是我们需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
interface Person {
name: string;
age?: number; //报错:类型“number | undefined”的属性“age”不能赋给“string”索引类型“string”。
[propName:string]:string
}
let p1: Person = {
name: 'Tom',
sex:'male'
};
这里我们的name和age就必须是string类型了。
我们在class中使用了readonly属性,代表这个属性只能在初始化的时候进行赋值,之后就不能对其进行修改了,而在接口中也有readonly属性,效果和在class中是一样的。
interface Person {
readonly name: string;
age?: number;
[propName:string]:any
}
let p1: Person = {
name: 'Tom',
sex:'male'
};
p1.name = 'xiaoxu'; //报错:无法分配到 "name" ,因为它是只读属性。
接口也有继承
之前我们说过类型类可以使用&实现继承,而接口也可以使用&来实现继承,那除此之外,接口还可以和class一样,使用extends来完成继承:
interface Person {
name: string;
age?: number;
}
interface Man extends Person {
sex: string
}
let p1 = {
name:'xiaolei',
age:20,
sex:'male'
}
类不仅仅是类
我们知道我们在定义一个对象的时候,可以事先声明好这个对象的类型,一般都是使用类型别名或者接口的方式:
type Person = {
name:string,
age:number,
sayName:()=>void
}
let p:Person = {
name:'xiaolei',
age:20,
sayName() {
console.log(this.name);
}
}
------------------
interface Person {
name:string,
age:number,
sayName:()=>void
}
let p:Person = {
name:'xiaolei',
age:20,
sayName() {
console.log(this.name);
}
}
但是如果我们的对象是一个类构造出来的,我们也得去事先声明类型吗?答案是不用的,因为类Class就相当于一个接口:
class Person {
public name:string;
public age:number;
constructor(name:string,age:number) {
this.name = name;
this.age = age;
}
sayName():void {
console.log(this.name);
}
}
let p:Person = new Person('xiaolei',20);
甚至不是Person的实例我们也可以用class:
let p2:Person = {};//报错:类型“{}”缺少类型“Person”中的以下属性: name, age, sayName
--------------------
let p2:Person = {
name:'xiaolei',
age:20,
sayName() {
console.log(this.name)
}
}
所以呢,我们在TS中创建一个类的时候,不仅仅是创建了这个class类,还创建了一个名字为类名的类型(实例类型)。也就是说上面的class Person和interface Person在某种意义上来说是一样的。
class与interface的关系
我们现在对于class和interface的单独使用都应该比较熟悉了,也说了class和interface在某种意义上是一样的,那这两者之间是不是可以有什么关联呢?答案是肯定的,我们的class里面有个特殊的类---抽象类,就是我们在父类声明公共的方法和属性,而不同的子类去实现它的方式都不一样,那么在这里接口就可以达到和抽象类作用差不多的效果:
interface Person {
name: string;
age: number;
sayName:()=>void
}
class Man implements Person {}
//报错:类“Man”错误实现接口“Person”。
// 类型“Man”缺少类型“Person”中的以下属性: name, age, sayName
---------------------------
abstract class Person {
public name;
public age;
constructor(name:string,age:number) {
this.name = name;
this.age = age;
}
abstract sayName():void;
}
class Man extends Person {} //报错:非抽象类“Man”不会实现继承自“Person”类的抽象成员“sayName”。
但是有时候使用接口比使用抽象类更好,因为我们的每一个类都只能继承一个父类,但是我们实现接口却可以实现多个:
interface Person {
name: string;
age: number;
sayName:()=>void
}
interface Eat {
foodName:string
}
class Man implements Person,Eat {
constructor(public name:string,public age:number,public foodName:string){
this.name = name;
this.age = age;
this.foodName = foodName;
}
sayName() {
console.log(this.name);
}
}
所以有时候接口也是有大用的。
接口继承类
除此之外,我们的接口除了能继承接口外,还可以继承类:
class Person {
constructor(public name:string,public age:number){
this.name = name;
this.age = age;
}
}
interface Man extends Person {
sex:string
}
let p1:Man = {} //报错:类型“{}”缺少类型“Man”中的以下属性: sex, name, age
所以我说在某些方面,class和interface在某种意义上来说的一样的(声明类型方面)。至于原因嘛,就是class Person不仅仅定义了class类,还声明了Person类型。它就等同于:
class Person {
constructor(public name:string,public age:number){
this.name = name;
this.age = age;
}
}
interface PersonInterface {
name:string,
age:number
}
interface Man extends PersonInterface {
sex:string
}
let p1:Man = {} //报错:类型“{}”缺少类型“Man”中的以下属性: sex, name, age
let p2 = new Person('xiaolei',20);
这里的PersonInterface与创建class Person时创建的Person类型的等价的,所以我们的interface Man extendes Person其实就是interface Man extends PersonInterface。
但是要注意的是,class创建的类在隐式的创建类型时,不会包含constructor构造函数、静态属性以及静态方法的。