一、什么是接口

https://segmentfault.com/a/1190000010979494

接口的声明

在前面我们通过type可以用来声明一个对象类型

对象的另外一种声明方式就是通过接口来声明

  1. type InfoType = {
  2. name: string;
  3. age: number;
  4. };
  5. interface IInfoType {
  6. name: string;
  7. age: number;
  8. }
  9. let p: InfoType = {
  10. name: 'cc',
  11. age: 79,
  12. };
  13. let p2: IInfoType = {
  14. name: 'bb',
  15. age: 90,
  16. };
  17. export {};

一般以I开头表明是个接口。

索引类型

使用interface来定义对象类型,这个时候其中的属性名、类型、方法都是确定的,但是有时候我们会遇到类似下面的对象

  1. // 通过interface来定义索引类型
  2. interface IndexLanguage {
  3. [index: number]: string
  4. }
  5. const frontLanguage: IndexLanguage = {
  6. 0: "HTML",
  7. 1: "CSS",
  8. 2: "JavaScript",
  9. 3: "Vue"
  10. }
  11. interface ILanguageYear {
  12. [name: string]: number
  13. }
  14. const languageYear: ILanguageYear = {
  15. "C": 1972,
  16. "Java": 1995,
  17. "JavaScript": 1996,
  18. "TypeScript": 2014
  19. }

函数类型

前面我们都是通过interface来定义对象中普通的属性和方法的,实际上它也可以用来定义函数类型

  1. // type CalcFn = (n1: number, n2: number) => number
  2. // 可调用的接口
  3. interface CalcFn {
  4. (n1: number, n2: number): number
  5. }
  6. function calc(num1: number, num2: number, calcFn: CalcFn) {
  7. return calcFn(num1, num2)
  8. }
  9. const add: CalcFn = (num1, num2) => {
  10. return num1 + num2
  11. }
  12. calc(20, 30, add)

除非特别的情况,还是推荐使用类型别名来定义函数

  1. type CalcFn = (n1: number, n2: number) => number

交叉类型

我们学习了联合类型:

联合类型表示多个类型中一个即可

  1. // 一种组合类型的方式: 联合类型
  2. type WhyType = number | string
  3. type Direction = "left" | "right" | "center"

还有另外一种类型合并,就是交叉类型(Intersection Types)

交叉类似表示需要满足多个类型的条件

交叉类型使用 & 符号

我们来看下面的交叉类型

  1. // 另一种组件类型的方式: 交叉类型
  2. type WType = number & string

表达的含义是number和string要同时满足

但是有同时满足是一个number又是一个string的值吗?其实是没有的,所以MyType其实是一个never类型

在开发中,我们进行交叉时,通常是对对象类型进行交叉的

  1. interface ISwim {
  2. swimming: () => void
  3. }
  4. interface IFly {
  5. flying: () => void
  6. }
  7. type MyType1 = ISwim | IFly
  8. type MyType2 = ISwim & IFly
  9. const obj1: MyType1 = {
  10. flying() {
  11. }
  12. }
  13. const obj2: MyType2 = {
  14. swimming() {
  15. },
  16. flying() {
  17. }
  18. }

接口的继承

接口和类一样是可以进行继承的,也是使用extends关键字

并且接口是支持多继承的(类不支持多继承)

  1. interface ISwim {
  2. swimming: () => void
  3. }
  4. interface IFly {
  5. flying: () => void
  6. }
  7. interface IAction extends ISwim, IFly {
  8. }
  9. const action: IAction = {
  10. swimming() {
  11. },
  12. flying() {
  13. }
  14. }

接口的实现

接口定义后,也是可以被类实现的

如果被一个类实现,那么在之后需要传入接口的地方,都可以将这个类传入

这就是面向接口开发

  1. interface ISwim {
  2. swimming: () => void
  3. }
  4. interface IEat {
  5. eating: () => void
  6. }
  7. // 类实现接口
  8. class Animal {
  9. }
  10. // 继承: 只能实现单继承
  11. // 实现: 实现接口, 类可以实现多个接口
  12. class Fish extends Animal implements ISwim, IEat {
  13. swimming() {
  14. console.log("Fish Swmming")
  15. }
  16. eating() {
  17. console.log("Fish Eating")
  18. }
  19. }
  20. class Person implements ISwim {
  21. swimming() {
  22. console.log("Person Swimming")
  23. }
  24. }
  25. // 编写一些公共的API: 面向接口编程
  26. function swimAction(swimable: ISwim) {
  27. swimable.swimming()
  28. }
  29. // 1.所有实现了接口的类对应的对象, 都是可以传入
  30. swimAction(new Fish())
  31. swimAction(new Person())
  32. swimAction({swimming: function() {}})

interface和type的区别

我们会发现interface和type都可以用来定义对象类型,那么在开发中定义对象类型时,到底选择哪一个呢

如果是定义非对象类型

  • 通常推荐使用type,比如Direction、Alignment、一些Function

如果是定义对象类型,那么他们是有区别

  • interface 可以重复的对某个接口来定义属性和方法
  • 而type定义的是别名,别名是不能重复的
  1. // interface定义对象类型允许别名
  2. interface IFoo {
  3. name: string
  4. }
  5. interface IFoo {
  6. age: number
  7. }
  8. // 相当于合并
  9. const foo: IFoo = {
  10. name: "why",
  11. age: 18
  12. }
  13. // Window Document Date等类型是ts自己的库实现的
  14. // 所以interface Window{} 相当于合并 - - 不建议这么做
  15. document.getElementById("app") as HTMLDivElement
  16. window.addEventListener
  17. interface Window {
  18. age: number
  19. }
  20. window.age = 19
  21. console.log(window.age)
  22. //type定义对象类型不允许别名
  23. // type IBar = {
  24. // name: string
  25. // age: number
  26. // }
  27. // type IBar = {
  28. // }
  29. interface IPerson {
  30. }

字面量赋值

TypeScript在字面量直接赋值的过程中,为了进行类型推导会进行严格的类型限制

但是之后如果我们是将一个变量标识符赋值给其他的变量时,会进行freshness擦除操作

  1. interface IPerson {
  2. name: string
  3. age: number
  4. height: number
  5. }
  6. const info = {
  7. name: "why",
  8. age: 18,
  9. height: 1.88,
  10. address: "广州市"
  11. }
  12. // freshness擦除
  13. // 相当于把address这个类型擦擦除了
  14. const p: IPerson = info
  15. console.log(info)
  16. console.log(p)
  17. // 应用
  18. function printInfo(person: IPerson) {
  19. console.log(person)
  20. }
  21. // 代码会报错 因为多了address类型
  22. // printInfo({
  23. // name: "why",
  24. // age: 18,
  25. // height: 1.88,
  26. // address: "广州市"
  27. // })
  28. const info1 = {
  29. name: "why",
  30. age: 18,
  31. height: 1.88,
  32. address: "广州市"
  33. }
  34. //这样可以 因为address被擦除
  35. printInfo(info1)

枚举类型

枚举类型是为数不多的TypeScript特性有的特性之一

枚举其实就是将一组可能出现的值,一个个列举出来,定义在一个类型中,这个类型就是枚举类型

枚举允许开发者定义一组命名常量,常量可以是数字、字符串类型

  1. // type Direction = "left" | "Right" | "Top" | "Bottom"
  2. enum Direction {
  3. LEFT,
  4. RIGHT,
  5. TOP,
  6. BOTTOM
  7. }
  8. function turnDirection(direction: Direction) {
  9. switch (direction) {
  10. case Direction.LEFT:
  11. console.log("改变角色的方向向左")
  12. break;
  13. case Direction.RIGHT:
  14. console.log("改变角色的方向向右")
  15. break;
  16. case Direction.TOP:
  17. console.log("改变角色的方向向上")
  18. break;
  19. case Direction.BOTTOM:
  20. console.log("改变角色的方向向下")
  21. break;
  22. default:
  23. const foo: never = direction;
  24. break;
  25. }
  26. }
  27. turnDirection(Direction.LEFT)
  28. turnDirection(Direction.RIGHT)
  29. turnDirection(Direction.TOP)
  30. turnDirection(Direction.BOTTOM)

枚举类型的值

枚举类型默认是有值的,比如上面的枚举,默认值是 0 1 2 3 ….:

当然,我们也可以给枚举其他值:

也可以给他们赋值其他的类型:

  1. // type Direction = "left" | "Right" | "Top" | "Bottom"
  2. enum Direction {
  3. LEFT = "LEFT",
  4. RIGHT = "RIGHT",
  5. TOP = "TOP",
  6. BOTTOM = "BOTTOM"
  7. }
  8. let name: string = "abc"
  9. let d: Direction = Direction.BOTTOM
  10. function turnDirection(direction: Direction) {
  11. console.log(direction)
  12. switch (direction) {
  13. case Direction.LEFT:
  14. console.log("改变角色的方向向左")
  15. break;
  16. case Direction.RIGHT:
  17. console.log("改变角色的方向向右")
  18. break;
  19. case Direction.TOP:
  20. console.log("改变角色的方向向上")
  21. break;
  22. case Direction.BOTTOM:
  23. console.log("改变角色的方向向下")
  24. break;
  25. default:
  26. const foo: never = direction;
  27. break;
  28. }
  29. }
  30. turnDirection(Direction.LEFT)
  31. turnDirection(Direction.RIGHT)
  32. turnDirection(Direction.TOP)
  33. turnDirection(Direction.BOTTOM)
  34. export {}