typescrip接口 interface详解,以及ts实现多态

ts 接口

当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的

示例如下

当一个对象类型被多次使用时,可以看到,很明显代码有大量的冗余

let personTom: { name: string, age?: number, sayHi(name: string): void } = { name: 'Tom',
  sayHi(name: string) { console.log(`Hi, ${name}`)
  }
}
let personJack: { name: string, age?: number, sayHi(name: string): void } = { name: 'Tom',
  sayHi(name: string) { console.log(`Hi, ${name}`)
  }
}

这个时候可以将这个对象定义为接口,以进行复用,可以看到,这样代码就少了很多冗余

interface Person { name: string
  age?: number
  sayHi(name: string): void
}
let personTime: Person = { name: 'time',
  sayHi(name: string) { console.log(`hello ${name}`)
  }
}
let personJohn: Person = { name: 'John',
  sayHi(name: string) { console.log(`hello ${name}`)
  }
}
  1. 使用interface关键字来声明接口
  2. 接口名称(比如,此处的Person)可以是任意合法变量名称
  3. 声明接口后,直接使用接口名称作为变量的类型
  4. 因为每一行只有一个属性类型,因此,属性类型后没有分号或逗号

interface 与 type 类型别名的区别

在 TypeScript中,interface和type都可以用来定义类型的别名

  • 定义方式:type别名可以用来给一个类型起新名字,使用type创建类型别名。它更加灵活,可以用来定义任意类型的别名,包括原始类型、函数、对象等。而interface则是命名数据结构的另一种方式,仅限于描述对象类型,声明语法也不同于type的声明语法。
  • 使用范围:与type不同,interface仅限于描述对象类型。也就是说,interface无法用来定义非对象类型的别名,如原始类型、函数等。type则没有这些限制,可以用来定义各种类型的别名。
  • 组合类型:在TypeScript中,type可以使用交叉类型(intersection type)和联合类型(union type)来组合多个类型,而interface则不能。这意味着type可以创建更复杂和灵活的类型结构,而interface在这方面的能力较弱。

    总的来说,type和interface在TypeScript中都可以用来定义类型的别名,但它们在定义范围、组合类型的能力等方面存在明显的差异。

    interface(接口)和type(类型别名)的对比

    • 相同点: 都可以给对象指定类型
    • 不同点:

      - 接口只能为对象指定类型

      - 类型别名,不仅可以为对象指定类型,实际上可以给任意类型指定别名

      代码示例

      interface Person { name: string
        age?: number
        sayHi(name: string): void
      }
      type animal = { name: string
        age?: number
        sayHi(name: string): void
      }
      

      在编译器中使用,两者都可以实现对对象的类型监测

      接口的继承

      如果两个接口之间有相同的属性或方法,可以将公共的属性或方法,抽离出来,通过继承来实现复用

      比如,这两个接口都有x,y两个属性,重复写两次,可以,但是很繁琐

      interface Point2D { x: number, y: number }
      	interface Point3D { x: number, y: number, z: number }
      

      这个时候就可以使用extends继承来让Point3D 继承Point2D 就可以省去x和y的定义了

      如下

      interface Point2D { x: number, y: number }
      	// interface Point3D { x: number, y: number, z: number }
      	interface Point3D extends Point2D { z: number }
      

      tips:

      1. 使用extends(继承)关键字实现了接口Point3D 继承Point2D
      2. 继承后,Point3D 就有了Point2D的所有方法和属性了(此时Point3D 同时有x,y,z三个属性)
      继承多个接口

      一个接口可以继承多个接口,如下 video3D继承了video接口和3D接口 ,继承后,Video3D接口就同时拥有两个接口的所有属性和方法了

      interface Point2D { x: number, y: number }
      // interface Point3D { x: number, y: number, z: number }
      interface Point3D extends Point2D { z: number }
      interface Video { video: object }
      interface Video3D extends Video, Point3D { lookAt(target: Point3D): void;
      }
      let v: Video3D = { video: {}, x: 10, y: 10, z: 10, lookAt: (t) => { } } 

      typescript 多态

      先看下面这个例子

      interface Animal { name: string;  
          age: number;  
          sound: () => void;  
      }  
        
      interface Dog extends Animal { breed: string;  
      }  
        
      let myDog: Dog = { name: "Rex",  
          age: 3,  
          breed: "German Shepherd",  
          sound: () => console.log("Bark!")  
      };
      

      在这个例子中,Dog 接口继承了 Animal 接口。这意味着,Dog 对象必须包含 Animal 接口定义的所有属性和方法,也就是 name、age 和 sound。然后,Dog 接口还定义了自己的额外属性,即 breed。

      这是一个很有意思的现象,因为这已经是静态类型语言才能实现多态的基础了

      如上在 TypeScript 中,接口继承可以实现多态性。如果你有一个函数接受 Animal 类型的参数,那么你也可以传入一个 Dog 类型的参数,因为 Dog 是 Animal 的子类型。这是基于 Liskov 替换原则,也就是子类型必须能够替换它们的基类型。

      原理有了,开始实现

      //定义基类
      	interface Animal { name: string;
      	  age: number;
      	  sound: () => void;
      	}
      	
      	//定义基础
      	interface Dog extends Animal { breed: string;
      	}
      	
      	let myDog: Dog = { name: "Rex",
      	  age: 3,
      	  breed: "German Shepherd",
      	  sound: () => console.log("Bark!")
      	};
      	
      	//实现多态
      	function polymorphicDisplay(a: Animal) { a.sound();
      	}
      	
      	polymorphicDisplay(myDog);
      

      输出成功, js牛逼! ,不对ts牛逼