TypeScript入门之接口

717 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

简介

TypeScript里,接口的作用就是类型命名和为你的代码或第三方代码定义契约。

在接口中我们只需要定义属性或方法,不需要具体的实现。

一般我们用接口来定义对象类型和方法类型。

接口定义对象类型

我们使用interface关键字来定义接口。

interface User {
  name: string
}

上面就定义了一个User接口。

// 定义一个user对象,它的类型是User类型
const user: User = {name: 'randy'}

必选属性和必选方法

必选属性和必选方法就是我们接口定义的属性和方法,具体的对象必须有这些属性和方法,不然在编辑阶段会报错。

interface User {
  name: string; // 必须属性
  hi: (num: number) => number; // 必须函数,返回值为number
  hi2: () => void; // 必须函数,没有返回值的方法
  hi3(): void; // 必须函数,没有返回值的方法,简写形式
}

我们使用User接口定义对象的时候就必须具有这四个属性。否则在编译阶段就会报错。

let user: User = {
  name: 'randy',
  hi: (num: number) => num,
  hi2: () => {console.log('hi2')},
  hi3: () => {console.log('hi3')}
}

那有时候我们的属性或方法不是确定的呢?那就需要用到可选属性和可选方法啦。

可选属性和可选方法

可选属性和可选方法我们只需要在定义属性的时候使用?表示,跟前面函数章节说的可选参数是一样的。

interface User {
  name?: 'randy',
  hi?: (num: number) => number,
}

上面的例子,我们定义对象的时候就可以没有name属性和hi方法。

let user: User = {}

只读属性和只读方法

只读属性和只读方法就是我们定义的属性和方法是不能被修改的。我们使用readonly关键字来定义只读属性和只读方法。

interface User {
  readonly name: 'randy',
  readonly hi: (num: number) => number,
}

只读属性和方法也是必选属性和必选方法,也就是定义对象的时候是必须定义的,不然会报错。

和必选属性和必选方法的区别就是,必选属性和必选方法定义完后可以重新赋值,但是只读属性和只读方法就不能再重新赋值了。

let user: User = {
  name: 'randy',
  hi: (num: number) => num,
}

user.name = 'demi' // error 无法分配,因为它是只读属性
user.hi = (num: number) => num + 1 // error 无法分配,因为它是只读属性

但是如果只是必选属性和必选方法我们是可以重新赋值的。

任意属性和任意方法

因为js灵活的缘故,我们使用js的时候经常会在使用中途添加属性和方法,所以就有了不确定性。但是我们的ts又是在使用前必须前明确定义属性和方法,这就产生了冲突,那我们想在使用中途添加属性和方法该怎么办呢?

这就需要用到索引签名啦。

interface User {
  name: string;
  [prop: string]: any;
}

这里我们使用[prop: string]: any定义了一个索引签名。它表示可以接收任意的属性名,只要是字符串类型的就可以,接收任意的属性值。

let user: User = {name: 'randy', age: 24 }

// 还能动态添加任意属性和方法
user.count = 1
user.count2 = 12
user.say = () => {console.log("haha")}

// key只能是string,所以不能为symbol类型
user[Symbol(1)] = 123 // Error

有了索引签名,大大提高了我们的灵活性。

需要注意,索引签名参数类型必须是 “string”、“number”、“symbol”或模板文本类型

必选属性和可选属性值的类型必须是任意属性值类型的子集

索引签名还可以有更多用法。比如我们有个user对象,它的联系方式有很多种,不同的人有不同的情况。

// 张三
{ 
  name: '张三', 
  concat: {
    wechat: 'xiaozhang@163.com', 
    qq: '1845751425@qq.com', 
    // ...
  }
}

// 李四
{ 
  name: '李四', 
  concat: {
    phone: '17673298765', 
    qq: '1845751425@qq.com', 
    // ...
  }
}

我们发现,concat对象的键始终是string,值也始终是string,这时我们就可以为concat属性定义一个索引类型的接口。

interface Concat {
  [prop: string]: string
}

这样我们的User接口就可以使用Concat接口来定义我们的concat属性啦。

interface User {
  name: string;
  concat: Concat
}

接口定义函数类型

我们使用接口除了可以定义对象类型外,还可以定义函数类型。

interface Say {
  (str: string) : string
}

上面定义了一个接收一个string参数并返回string类型的函数。

const say: Say = (str) => str

接口定义构造函数

接口除了定义普通函数,还可以定义构造函数。

interface ToString {
  new (): string;
}

declare const sometingToString: ToString;

new sometingToString(); // ok

接口的继承

接口是可以继承的,学过java的同学肯定清楚。

interface User1 {
  name: string
}

interface User2 extends User1 {
  age: number
}

// 这样User2类型就既有了 name 属性又有了 age 属性
const user: User2 = {name: 'randy', age: 24}

并且接口是支持多继承的。

interface User3 extends User1, User2 {
  sex: string
}

这样我们的接口User3就同时具备了name、age、sex

接口和类型别名type的区别

说到这里很多小伙伴们肯定会有疑惑了,为什么有了类型别名还要有接口呢?不都是用来定义类型吗?

这里笔者说说自己的见解。

接口能实现继承

虽然接口和type都能定义类型,但是接口可以使用继承,相当于多个接口组合,让我们的代码更具灵活性。但是type是不支持的。

interface User3 extends User1, User2 {
  sex: string
}

接口能多次定义

interface User2 {
  name: string;
}

interface User2 {
  age: number;
}

// User2 同时具备name和age属性
const user2: User2 = { name: "randy", age: 24 };

// 会报错 type不能重复定义
// type User2 = { name: string };
// type User2 = { age: number };

接口能被实现

接口能被class来实现。type是不支持的。

class People implements User2 {
  name = "randy";
  age = 24;
}

关于实现,笔者再后面讲述class的时候会细说。

类型别名支持给原始类型取别名

这句话什么意思呢?笔者举个简单的例子就知道了。

// 给string原始类型取别名
type newstring = string;

const str2: newstring = "randy";
const str3: string = "randy";

console.log(str2, str3);

上面的例子,我们用给string类型去了别名 newstring。虽然type支持这样做但是我们平时开发中基本上不会这么去用。

大多数是在联合类型的时候使用type去重新定一下。

type NumAndStr = number | string;

总体来说,笔者觉得简单场景我们就使用type,复杂场景咱们就选择接口。

系列文章

TypeScript入门之环境搭建

TypeScript入门之数据类型

TypeScript入门之函数

TypeScript入门之接口

TypeScript入门之类

TypeScript入门之类型推断、类型断言、双重断言、非空断言、确定赋值断言、类型守卫、类型别名

TypeScript入门之泛型

TypeScript入门之装饰器

TypeScript入门之模块与命名空间

TypeScript入门之申明文件

TypeScript入门之常用内置工具类型

TypeScript入门之配置文件

后记

感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!