接口与类型

121 阅读3分钟

接口的定义

使用 interface 关键字定义:

// 先定义一个接口
interface IUser {
  name: string;
  age: number;
}

const getUserInfo = (user: IUser): string => {
  return`name: ${user.name}, age: ${user.age}`;
};

// 正确的调用
getUserInfo({name: "koala", age: 18});

定义两个接口

type IUserInfoFunc = (user: IUser) =>string;

interface IUser {
  name: string;
  age: number;
}

const getUserInfo: IUserInfoFunc = (user) => {
  return`name: ${user.name}, age: ${user.age}`;
};

getUserInfo({name: "koala", age: 18});

定义方法

interface IQuery {
  page: number;
  findOne(): void;
  findAll(): void;
}

如果我们有一个对象是该接口类型,那么必须包含对应的属性和方法(无可选属性情况):

const q: IQuery = {
  page: 1,
  findOne() {
    console.log("findOne");
  },
  findAll() {
    console.log("findAll");
  },
};

可选属性

注意:可选属性如果没有赋值,那么获取到的值是 undefined;对于可选方法,必须先进行判断,再调用,否则会报错

interface IQuery {
  page: number;
  findOne(): void;
  findAll(): void;
  isOnline?: string | number;
  delete?(): void
}
const q: IQuery = {
 page: 1,
 findOne() {
   console.log("findOne");
 },
 findAll() {
   console.log("findAll");
 },
};

console.log(p.isOnline); // undefined
p.delete(); // 不能调用可能是“未定义”的对象。

正确的调用方式如下:
if (p.delete) {
  p.delete();
}

大家可能会问既然是可选属性,可有可无的,那么为什么还要定义呢?对比起完全不定义,定义可选属性主要是:为了让接口更加的灵活,某些属性我们可能希望设计成可选,并且如果存在属性,能约束类型,而这也是十分关键的。

只读属性

interface IQuery {
  readonly page: number;
  findOne(): void;
}

page 属性加了 readonly 关键字,再给它赋值会报错。

const q: IQuery = {
  page: 1,
  findOne() {
    console.log("findOne");
  },
};
q.page = 10;// Cannot assign to 'page' because it is a read-only property.

函数类型的接口

interface 还可以用来规范函数,interface 里面需要列出参数列表返回值类型的函数定义。写法如下:

interface Func {
    // ✔️ 定义这个函数接收两个必选参数都是 number 类型
    // 以及一个可选的字符串参数 desc,这个函数不返回任何值
    (x: number, y: number, desc?: string): void
}

const sum: Func = function (x, y, desc = '') {
    console.log(desc, x + y)
}

sum(32, 22)

注意:不过上面的接口中只有一个函数,TypeScript 会给我们一个建议,可以使用 type 来定义一个函数的类型:

type Func = (x: number, y: number, desc?: string) =>void;

接口的实现

接口除了定义某种类型规范,也可以和其他编程语言一样,让一个类去实现某个接口,那么这个类就必须明确去拥有这个接口中的属性和实现其方法:

interface Entity {
  title: string;
  log(): void;
}
class Post implements Entity {
  title: string;

  constructor(title: string) {
    this.title = title;
  }

  log(): void {
    console.log(this.title);
  }
}

接口的继承

和类一样,接口也能继承其他的接口,这相当于复制接口的所有成员。接口也是用关键字 extends 来继承。

interface Shape {     //定义接口Shape
    color: string;
}

interface Square extends Shape {  //继承接口Shape
    sideLength: number;
}

一个 interface 可以同时继承多个 interface ,实现多个接口成员的合并,用逗号隔开要继承的接口。

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

需要注意的是,尽管支持继承多个接口,但是如果继承的接口中,定义的同名属性的类型不同的话,是不能编译通过的。如下代码:

interface Shape {
    color: string;
    test: number;
}

interface PenStroke extends Shape{
    penWidth: number;
    test: string;
}

typeinterface 的区别

type 可以而 interface 不可以

  1. type 可以声明基本类型别名,联合类型,元组等类型
// 基本类型别名
type Name = string

// 联合类型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
type Person = "Tom" | "Bob" | "Joy""
const person: Person = 'tom' as 'Tom'

// Person 的类型值只能是 "Tom"、"Bob"、"Joy" 其中的一个
// person 的类型是 "Tom"
  1. type 语句中还可以使用 typeof 获取实例的类型进行赋值
// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div
  1. type 其他操作
type StringOrNumber = string | number;
type Text = string | { text: string };
type NameLookup = Dictionary<string, Person>;
type Callback<T> = (data: T) =>void;
type Pair<T> = [T, T];
type Coordinates = Pair<number>;
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };

interface 可以而 type 不可以

  1. interface 能够声明合并
interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/*
User 接口为 {
  name: string
  age: number
  sex: string
}
*/
  1. interface 可以继承
interface Shape {     //定义接口 Shape
    color: string;
}

interface Square extends Shape {  //继承接口 Shape
    sideLength: number;
}