TypeScript:接口和多态
什么是接口
一、接口定义函数变量类型
//函数类型接口 interface addType { (bValue: number, secValue: number):number } function add(x: number, y: number): number { return x + y; } let myadd1:addType = add; let myadd2:addType = (x:number, y:number):number=>{ return x+y; }
二、接口与对象
(1)对象是字面量对象, 对象中成员必须与接口中声明的成员完全相同,不可多,也不可少
interface IPerson { firstName:string, lastName:string, sayHi: ()=>string , [prop:string]:any;//你可以随便扩展 值是任意类型 的 任意属性 } let teacher:IPerson = { firstName:"Tom", lastName:"Hanks", sayHi: ():string =>{return "Hi there"} } console.log(teacher);
以下代码也是对的
let teacher: { firstName:string, lastName:string, sayHi: ()=>string } = { firstName:"Tom", lastName:"Hanks", sayHi: ():string =>{return "Hi there"} }
(2)对象是通过new 出的对象, 对象中成员必须包含接口中声明的成员不可少,但是可以增加接口中未声明的成员
interface IPerson { firstName:string, lastName:string, sayHi: ()=>string } class Teacher { firstName:string="小五"; lastName:string="小二"; age:number= 30; //接口中没有声明,但是不会错 sayHi():string { return "Hi there"; } } let teacher:IPerson = new Teacher(); console.log(teacher);
以下代码也是对的
let teacher:{ firstName:string, lastName:string, sayHi: ()=>string } = new Teacher();
(3)类型断言情况下。对象中成员只能是接口中声明过的成员,但是可少
对于下面示例中stu对象中的成员可以包含firstName,lastName,sayHI, 可以少,不能出现接口中没有出现过的
interface IPerson { firstName:string, lastName:string, sayHi: ()=>string } let stu:IPerson = <IPerson> {}; stu.firstName = "司空"; stu.sayHi = ():string =>{return "Hi there"} console.log(stu); //stu.xx = "dfds"//错,因为接口中没有出现过
三、接口与数组
接口可以规定数组中元素的索引类型和元素类型,先看一个与默认情况相同的示例
//规定以number为元素索引、string为元素类型 interface Inamelist { [index:number]: string } let list: string[] = ["John","Bran"] list[2] = "jeep"; console.log(list); console.log(list[0]); console.log(list[1]); console.log(list[2]);
也可以使用非number类型为元素索引
//规定以string为元素索引、string为元素类型 interface Inamelist { [index: string]: string } let list: Inamelist = [] as unknown as Inamelist; list['a'] = "好的"; list['b'] = "不错的"; console.log(list); console.log(list['a']);//好的 console.log(list["b"]);//不错的
四、接口继承接口
接口继承接口后,子接口就除了自身接口声明之外还包含了父接口声明
interface Person {
age:number
}
interface Musician extends Person {
instrument:string
}
接口继承接口是可以多继承的
interface IParent1 { v1:number } interface IParent2 { v2:number } //Iparent1和Iparent2顺序是无关仅要的 interface Child extends IParent1, IParent2 { v3:string }
五、类实现接口
类可以实现接口,并可以多实现
类实现接口后,类体中必须实现接口所有字段和方法,当然类可以扩展自己本质的成员
interface IShape { draw():void; size:number; } interface IName { name: string; } class Circle implements IShape, IName { public size:number = 30; public name:string = "圆"; public draw():void { console.log("会绘制一个"+this.name); } public show() { console.log("本来是Circle"); } } let c:Circle = new Circle(); c.draw(); c.size = 31; c.name = "四边型"; c.show();
类可以继承父类的同时实现接口
interface IShape { draw():void; size:number; } interface IName { name: string; } class Shape { public showArea():void { console.log("面积是?平方") } } class Circle extends Shape implements IShape, IName { public size:number = 30; public name:string = "圆"; public draw():void { console.log("会绘制一个"+this.name); } public show() { console.log("本来是Circle"); } } let c:Circle = new Circle(); c.draw(); c.size = 31; c.name = "四边型"; c.showArea(); c.show();
六、 泛型函数类型接口(见泛型章节 )
//泛型函数类型接口1 interface GenericIdentityFn1 { (arg: T): T; } //泛型类函数型接口2 interface GenericIdentityFn2<T> { (arg: T): T; }
七、接口与泛型
八、接口变量指向实现类对象
只讲语法,关于接口变量指向实现类对象的核心思,见后面多态相关单节
接口变量可以引用接口实现类对象, 但是只能访问接口中声明过的成员, 接口变量也可赋值为null或undefined,
如果想使用此接口变量访问所有对象成员,那么可以对接口变量进行"类型断言"为对象所属类的类型。
interface IShape { draw():void; size:number; } interface IName { name: string; } class Circle implements IShape, IName { size:number = 30; name:string = "圆"; draw():void { console.log("会绘制一个"+this.name); } show() { console.log("本来是Circle"); } } let c:IShape = new Circle(); c.draw();//对,因为IShape中出现过 c.size = 31;//对,因为IShape中出现过 //c.name = "四边型";//错 //c.show();//错 let c2 = <Circle>c;//类型断言 c2.name = "四边型";//对 c2.show();//对
九、多态
面向对象核心思想是继承、封装与多态, 本小节只讲多态
1 概念
-
多态概念: 一种状态多种表现形式
-
方法中this在编译时并不知道是哪个对象,执行过程中动态确定, 因为是在执行过程中确定的this,所以this属于运行时多态。
-
"接口变量指向实现类对象"或"父类指向子类对象+重写",可以实现运行时多态。
-
有了接口变量引用对象,或父类变量引用子类对象,可以非常清晰的代码实现动态代理设计模式。
2 接口变量实现多态
下面示例, 在myshow方法中的 "a.show()"在编译的时候并不确定是执行X中的show()还是执行Y中的show(),还是其它类中的show, 只有运行过程中才能确定,完美实现动态多态性
interface A { show():void; } class X implements A { show() { console.log("这是X的show()") } } class Y implements A { show() { console.log("这是Y的show()") } } class Test { myshow(a: A) { //a=new X() a=new Y() a.show(); } } let x = new X(); let y = new Y(); let t = new Test(); t.myshow(x);//这是X的show() t.myshow(y);//这是Y的show()
3 父类变量+重写实现多态
下面示例中, 同样 myshow方法中的 "a.show()" 在运行过程中才知道是调用哪个类中的show()
class A { show() { console.log("A中的show") } } class X extends A { show() { super.show() console.log("这是X的show()") } } class Y extends A { show() { super.show(); console.log("这是Y的show()") } } class Test { myshow(a: A) {//a=new A() a=new X(); a=new Y(); a.show(); } } let y = new Y(); let t = new Test(); t.myshow(new X());//这是X的show() t.myshow(new Y());//这是Y的show() t.myshow(new A());//主是A的show()