Skip to content

interface

TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述

interface来定义一种约束,让数据的结构满足约束的格式。一般用于定义对象类型,也可以定义函数和数组

1.接口基本使用

使用interface来定义对象的形状(对象具有哪些特征和行为)

typeScript
// 定义IPerson接口
interface IPerson {
    name: string
    age: number
    say: () => void
}
//  使用IPerson接口对 对象zkp的形状进行约束 zkp对象中的属性不能少也不能多
//  必须符合接口IPerson的定义
const zkp:IPerson = {
    name: 'zkp',
    age: 11,
    say() {
        console.log('给我一个div')
    }
}

2.可选属性 ?

有时我们希望不要完全匹配一个形状,那么可以用可选属性,可选属性的含义是该属性可以不存在

格式: 属性名?: 属性值

typeScript
interface IPerson {
  name: string
  age?: number  // age为可选属性
}
// OK
const zkp: IPerson = {
  name: 'zkp',
}
// Ok
const yh: IPerson = {
  name: 'yh',
  age: 18
}

此时可选的属性可以没有 但仍然不允许添加未定义的属性:

typeScript
interface IPerson {
  name: string
  age?: number  // age为可选属性
}
/**
 * 报错:
 * 不能将类型“{ name: string; hobby: string[]; }”分配给类型“IPerson”。
 * 对象字面量只能指定已知属性,并且“hobby”不在类型“IPerson”中
 */
const zkp: IPerson = {
  name: 'zkp',
  hobby:['swimming']
}

3.索引签名-任意属性

有时候我们希望一个接口允许有任意的属性,可以使用如下方式:

typeScript
interface IPerson {
  readonly name: string
  age?: number
  /**
   * key:string 代表为string的任意属性  any代表任意属性值的类型
   * 注意点:在interface中其他属性的类型 必须是任意属性值类型的子类型:
   * 上面的number和string类型都是any的子类型
   */
  [key: string]: any  //定义索引签名
}
const zkp: IPerson = {
  name: 'zkp',
  age: 11,
  //  可以声明很多属性
  hobby: ['swimming'],
  xxx: 'hello'
}
/**
 *
 */

注意点:

  1. 在interface中其他属性的类型 必须是任意属性值类型的子类型! ! ! ! !
  2. [key: string]:any 中的key代表属性的类型 只能是string 或者 number 当为number的时候表明这个接口定义的是数组或者类数组
typeScript
// 这是定义了一个数组类型 可以理解为数组也是对象 但是数组的key是他的索引,是数字类型的
interface IP {
  [k: number]: any
}
const arr: IP = [2, 'zkp']

4.索引签名-接口数组

利用索引签名 可以使用interface定义数组和类数组

typeScript
interface IPerson {
  // 此时索引签名定义的键必须为number 在js和ts中键名为数字的对象只有数组和类数组
  [key: number]: any
}
// error
const obj: IPerson = {
  a: 9, //不能将类型“{ a: number; }”分配给类型“IPerson”。
}

// OK
const obj: IPerson = [1, 'zkp', 2, true]

// OK
const obj: IPerson = {
  0: 1,
  2: 'zkp',
  8: true,
}

5.调用签名-接口函数

为了使用接口表示函数类型,我们需要给接口定义一个调用签名。

它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型

typeScript
// 接口定义调用签名实现函数接口
interface ISing {
  (name: string, val: string | number): void
}
const sing: ISing = (name: string, val: string | number) => {
  console.log(`${name}在说${val}`)
}
sing('zkp', 'lg好帅')

使用调用签名可以给函数定义额外的属性:

typeScript
interface ISing {
  (name: string, val: string | number): void
  age: number
}
const sing: ISing = (name: string, val: string | number) => {
  console.log(`${name}在说${val}`)
}
// 属性age的实现写在外面
sing.age = 9

6.只读属性 readonly

有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性:

格式: readonly 属性名: 属性值

typeScript
interface IPerson {
  readonly name: string
  age?: number
}
const zkp: IPerson = {
  name: 'zkp',
  age: 11,
}
zkp.name = 'yh' // 无法为“name”赋值,因为它是只读属性。ts(2540)

使用 readonly 并不意味着一个值就完全是不变的,亦或者说,内部的内容是不能变的。readonly 仅仅表明属性本身是不能被重新写入的。上面的代码中使用了readonly只代表zkp对象中的name属性不可变

7.接口继承

  • 接口继承就是说接口可以通过其他接口来扩展自己。
  • Typescript 允许接口继承多个接口。继承的接口之间使用逗号分割
  • 继承使用关键字 extends
typeScript
interface IAnimal {
  loveliness: boolean
}
interface ISmall {
  small?: true
}
// ICat类型继承自 IAnimal和 ISmall类型
interface ICat extends IAnimal , ISmall {
  // ICat类型自己的属性 say
  say(val: string): void
}
// 此时cat对象中必须同时包含ICat,IAnimal中的所有属性,因为这两个类型的属性都不是可选的
// 还可以包含ISmall类型中的small属性,这个是可选的 ,可以没有
const cat: ICat = {
  say(val) {
    console.log('val', val)
  },
  loveliness: true,
  small: true
}

同名的接口声明不会报错,后面的声明会默认继承前面的声明

typeScript
interface IPerson {
  name: string
}
// 这个IPerson默认继承了上面声明的IPerson
interface IPerson {
  age: number
}
// 这里实现接口 IPerson的时候,对象必须同时包含name和age属性,因为他们都不是可选的
const zkp: IPerson = {
  name: 'zkp',
  age: 11,
}