TS接口

# TS接口

# 接口(Interface)是什么?

相比JavaScript,typescript中的接口(Interface)是一个新的概念,首先来了解一下接口到底是什么?

接口声明(interface declaration)是命名对象类型的另一种方式,它能将多个类型注解合并成一个类型注解。用来约束对象函数的结构。

下面分别用接口约束不同类型变量的结构

# 约束对象的结构

对象作为函数的参数,使用接口进行约束

interface Person {
    name: string;
    sayHello(): void;
}

function fn(per: Person){
    per.sayHello();
}

fn({name:'孙悟空', sayHello() {console.log(`Hello, 我是 ${this.name}`)}});

约束对象时还可以定义可选成员、只读成员、动态成员

interface Post {
    title: string
    content: string
    subtitle?: string        // 可选成员
    readonly summary: string // 只读成员
    [prop: string]: string   // 动态成员 【可索引类型】
}

const hello: Post = {
    title: 'yk',
    content: 'ykjun nb',
    summary: 'ykjun',
    yk: 'ykyk'
}

hello.kk = 'kk'

# 【补充】可索引类型

不确定对象中属性的个数时,可以使用可索引类型的接口

可索引类型具有一个索引签名,它描述了对象索引的类型,还有相应的索引返回值类型

比如:约束字符串数组

interface StringArr {
    [index: number]: string
}

let stringArr: StringArr = ['a', 'b', 'c']

# 约束函数的结构

interface Add {
    (a:number, b:number): number // 约束函数的类型结构
}

let add: Add = function(a,b){
    return a + b
}

比如对象中有一个字段是一个函数,使用接口约束就比较方便

interface Obj {
    name: string
    add: Add
}

# 约束对象数组

interface List {
    readonly id: string // 只读属性
    name: string // 末尾可以用逗号,其实应该用分号,所以可以省略符号
    sex?: string // 可选属性
    [x: string]: any // 字符串索引签名,让List可以支持多个属性
}

interface Result {
    data: List[]
}

function render(result: Result) {
    result.data.forEach((value)=>{
        console.log(value.id, value.name)
    })
}

let result: Result = {
    data: [
        { id: 1, name: 'YK', sex: 'male' },
        { id: 2, name: 'yk', phone: 1234 }
    ]
}

render(result)

# 接口与类

# 类可以实现接口

使用接口来约束类成员,使用implements实现接口,约束类的属性和方法的类型。类实现接口,必须要实现接口定义所有的属性。接口只能约束类的公有成员,不能约束类的构造函数。

interface Human {
  name: string
  eat(): void
}

class Asian implements Human {
  name: string
  constructor(name: string) {
    this.name = name
  }
  eat() {}
}

一个类还可以实现多个接口

interface Eat{
    eat(food: string): void
}

interface Run{
    run(distance: number): void
}


class Person implements Eat, Run {
    eat(food: string):void{
      console.log(`坐着吃饭: ${food}`)
    }
    
    run(distance: number) {
        console.log(`直立行走:${distance}`)
    }
}

class Animal implements Eat, Run {
    eat(food: string):void{
      console.log(`啃着吃: ${food}`)
    }
    
    run(distance: number) {
        console.log(`爬行:${distance}`)
    }
}

# 接口继承

对接口使用 extends关键字允许我们有效的从其他声明过的类型中拷贝成员,并且随意添加新成员。接口也可以继承多个类型

interface Human {
  name: string
  eat(): void
}

interface Man extends Human {
  run(): void
}
interface Child {
  cry(): void
}

// 继承多个接口
interface Boy extends Man, Child {}

let boy: Boy = {
  name: '',
  run() {},
  eat() {},
  cry() {},
}

接口还可以继承类,相当于把类的成员抽象出来(只有类的成员结构,没有具体实现)

class Auto {
  state = 1
  private state2 = 0
}

// 接口继承类
interface AutoInterface extends Auto {
  
}

// 类实现接口
class C implements AutoInterface {
  state = 1
}

类继承父类实现接口
class Bus extends Auto implements AutoInterface {

}

最后总结一下接口与类关系

  • 接口之间可以相互继承,这样能够实现接口的复用
  • 类之间可以相互继承,可以实现属性和方法的复用
  • 类可以实现接口,接口只能约束类的公有成员
  • 接口可以继承类的成员,包括公有、私有和受保护成员

image.png

# 接口与抽象类的区别

抽象类做为其它派生类的基类使用,它们一般不会直接被实例化,不同于接口,抽象类可以包含成员的实现细节

# 接口与类型别名

类型别名和接口非常相似,大部分时候,你可以任意选择使用。接口的几乎所有特性都可以在 type 中使用,两者最关键的差别在于类型别名本身无法添加新的属性,而接口是可以扩展的。

# 扩展

// Interface
// 通过继承扩展类型
interface Animal {
  name: string
}

interface Bear extends Animal {
  honey: boolean
}

const bear = getBear() 
bear.name
bear.honey
        
// Type
// 通过交集扩展类型
type Animal = {
  name: string
}

type Bear = Animal & { 
  honey: boolean 
}

const bear = getBear();
bear.name;
bear.honey;

# 添加新的属性

// Interface
// 对一个已经存在的接口添加新的字段
interface Window {
  title: string
}

interface Window {
  ts: TypeScriptAPI
}

const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {});
        
// Type
// 创建后不能被改变
type Window = {
  title: string
}

type Window = {
  ts: TypeScriptAPI
}

// Error: Duplicate identifier 'Window'.

接口可能只会被用于声明对象的形状,不能重命名原始类型

// Here's two examples of 
// using types and interfaces
// to describe an object 

interface AnObject1 {
    value: string
}

type AnObject2 = {
    value: string
}

// Using type we can create custom names
// for existing primitives:

type SanitizedString = string
type EvenNumber = number

// This isn't feasible with interfaces
interface X extends string {

}

接口与类型别名的区别更多阅读:常见类型_TypeScript中文文档 (yayujs.com) (opens new window)

上次更新: 2022/6/9 17:04:06