戳蓝字「前端技术优选」关注我们哦!

接口带来了什么好处

好处One —— 过去我们写 JavaScript

JavaScript 中定义一个函数,用来获取一个用户的姓名和年龄的字符串:

const getUserInfo = function(user) {return name: ${user.name}, age: ${user.age}}

函数调用:

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

这对于我们之前在写 JavaScript 的时候,再正常不过了,但是如果这个 getUserInfo 在多人开发过程中,如果它是个公共函数,多个开发者都会调用,如果不是每个人点进来看函数对应注释,可能会出现以下问题:

// 错误的调用getUserInfo() // Uncaught TypeError: Cannot read property 'name' of undefinedconsole.log(getUserInfo({name: "kaola"})) // name: kaola, age: undefinedgetUserInfo({name: "kaola", height: 1.66}) // name: koala, age: undefined

JavaScript 是弱类型的语言,所以并不会对我们传入的代码进行任何的检测,有些错你自己都说不清楚,但是就出了问题。

TypeScript 中的 interface 可以解决这个问题

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

正确的调用是如下的方式:

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

如果调用者出现了错误的调用,那么 TypeScript 会直接给出错误的提示信息:

// 错误的调用getUserInfo(); // 错误信息:An argument for 'user' was not provided.getUserInfo({name: "coderwhy"}); // 错误信息:Property 'age' is missing in type '{ name: string; }'getUserInfo({name: "coderwhy", height: 1.88}); // 错误信息:类型不匹配

这时候你会发现这段代码还是有点长,代码不便与阅读,这时候就体现了 interface 的必要性。

使用 interface 对 user 的类型进行重构。

我们先定义一个 IUser 接口:

// 先定义一个接口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});

//  错误的调用

getUserInfo();

好处TWO —— 过去我们用 Node.js 写后端接口

其实这个说明和上面类似,我再提一下,就是想证明 TypeScript 确实挺香的!写一个后端接口,我要特意封装一个工具类,来检测前端给我传递过来的参数,比如下图中的validate专门用来检验参数的函数但是有了 TypeScript 这个参数检验函数可以省略了,我们可以这样写:

const goodParams: IGoodsBody = this.ctx.body;

GoodsBody就是对应参数定义的 interface,比如这个样子

// -- 查询列表时候使用的接口interface IQuery {    page: number;    rows: number;    disabledPage?: boolean; // 是否禁用分页,true将会忽略`page`和`rows`参数 }// - 商品exportinterface IGoodsQuery extends Query {    isOnline?: string | number; // 是否出售中的商品    goodsNo?: string; // 商品编号    goodsName?: string; // 商品名称 }

好的,说了他的几个好处之后,我们开始学习interface知识吧!还有很多优点哦!

接口的基础篇

接口的定义

和 java 语言相同,TypeScript 中定义接口也是使用 interface 关键字来定义:

interface IQuery {  page: number;}

你会发现我都在接口的前面加了一个I,算是个人习惯吧,之前一直写 java 代码,另一方面tslint要求,否则会报一个警告,是否加看个人。

接口中定义方法

看上面的接口中,我们定义了 page 常规属性,定义接口时候不仅仅可以有 属性,也可以有方法,看下面的例子:

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

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

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

接口中定义属性

普通属性

上面的 page 就是普通属性,如果有一个对象是该接口类型,那么必须包含对应的普通属性。就不具体说了。

可选属性

默认情况下一个变量(对象)是对应的接口类型,那么这个变量(对象)必须实现接口中所有的属性和方法。

但是,开发中为了让接口更加的灵活,某些属性我们可能希望设计成可选的(想实现可以实现,不想实现也没有关系),这个时候就可以使用可选属性(后面详细讲解函数时,也会讲到函数中有可选参数):

interface IQuery {page: number;  findOne(): void;  findAll(): void;  isOnline?: string | number; // 是否出售中的商品delete?(): void}

上面的代码中,我们增加了isOnline属性和delete方法,这两个都是可选的:

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

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

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

正确的调用方式如下:

if (p.delete) {  p.delete();}

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

只读属性

默认情况下,接口中定义的属性可读可写:但是有一个关键字 readonly,定义的属性值,不可以进行修改,强制修改后报错。

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 = '') {// const sum: Func = function (x: number, y: number, desc: string): void {// ts类型系统默认推论可以不必书写上述类型定义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;}

另外关于继承还有一点,如果现在有一个类实现了 Square 接口,那么不仅仅需要实现 Square 的方法,也需要实现 Square 继承自的接口中的方法,实现接口使用 implements 关键字 。

可索引类型接口

interface和type的区别

type 可以而 interface 不行

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

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

type Pet = Dog | Cat

// 具体定义数组每个位置的类型type PetList = [Dog, Pet]
  • type 语句中还可以使用 typeof 获取实例的 类型进行赋值
// 当你想获取一个变量的类型时,使用 typeof

let div = document.createElement('div');type B = typeof div
  • type 其他骚操作
type StringOrNumber = string | number;type Text = string | { text: string };type NameLookup = Dictionary;type Callback = (data: T) =>void;type Pair = [T, T];type Coordinates = Pair;type Tree = T | { left: Tree, right: Tree };

interface 可以而 type 不行

interface 能够声明合并

interface User {name: string  age: number}

interface User {sex: string}

/*User 接口为 {  name: string  age: number  sex: string}*/

另外关于type的更多内容,可以查看文档:TypeScript官方文档

接口的应用场景总结

在项目中究竟怎么用,开篇已经举了两个例子,在这里再简单写一点,最近尝试了一下egg+ts,学习下。在写查询参数检验的时候,或者返回固定数据的时候,都会用到接口,看一段简单代码,已经看完了上面的文章,自己体会下吧。

import User from'../model/user';import Good from'../model/good';

// 定义基本查询类型// -- 查询列表时候使用的接口interface Query {page: number;    rows: number;    disabledPage?: boolean; // 是否禁用分页,true将会忽略`page`和`rows`参数  }

// 定义基本返回类型type GoodResult = {list: Entity[];    total: number;    [propName: string]: any;};// - 商品export interface GoodsQuery extends Query {    isOnline?: string | number; // 是否出售中的商品    goodsNo?: string; // 商品编号    goodsName?: string; // 商品名称}export type GoodResult = QueryResult;

总结

TypeScript 还是挺香的,预告一篇明天的发文吧,TypeScript强大的类型别名。

参考文章

https://juejin.im/post/5c8fbf516fb9a070d8781b3c#heading-6

https://www.teakki.com/p/57dfb5a0d3a7507f975ea2cc

https://juejin.im/post/5c2723635188252d1d34dc7d

https://mp.weixin.qq.com/s/aj45tr7AZkWYbFXyA-Ku-Q

http://cw.hubwiz.com/card/c/55b724ab3ad79a1b05dcc26c/1/5/4/

在看点这里

接口缺方法调用时报错_TypeScript真香系列——接口篇相关推荐

  1. U9接口服务被调用报错怎么查看

    当U9服务被调用时报错时我们怎么查看错误呢?  如报错如下:  在 UFSoft.UBF.View.Query.QueryContext.Translate(String oql, Globaliza ...

  2. sklearn安装成功,调用时报错NO Module “sklearn“

    sklearn安装成功,调用时报错NO Module "sklearn" 1.问题描述 2.解决办法 1.问题描述 在调试程序时,报错没有sklearn,然后用pip安装了skle ...

  3. 设计一个形状类(接口)Shape,方法:求周长和求面积形状类(接口)的子类(实现类):

    设计一个形状类(接口)Shape,方法:求周长和求面积 形状类(接口)的子类(实现类): package zuoye2;public interface Shape {// 求面积方法double g ...

  4. 设计一个形状类(接口)Shape,方法:求周长和求面积形状类(接口)的子类(实现类): Rect(矩形) Circle(圆形)Rect类的子类: Square(正方形)不同的子类会有不同的计算周长和面

    设计一个形状类(接口)Shape,方法:求周长和求面积 形状类(接口)的子类(实现类):     Rect(矩形)     Circle(圆形) Rect类的子类:     Square(正方形)   ...

  5. TypeScript真香系列-高级类型

    前言 TypeScript真香系列的内容将参考中文文档,但是文中的例子基本不会和文档中的例子重复,对于一些地方也会深入研究.另外,文中一些例子的结果都是在代码没有错误后编译为JavaScript得到的 ...

  6. android 小米相机问题吗,如果小米10把相机优化到位,那这款机器属于真香系列!...

    如果小米10把相机优化到位,那这款机器属于真香系列! 2020-05-12 22:42:42 32点赞 9收藏 15评论 今年小米10给人印象最深的是,它涨价了.发布会当时3999元起步就劝退了不少人 ...

  7. 「真香系列」新物种首发亮相 聚划算爆款孵化玩法升级

    从普通商品到优质好物,差的便是那一句"真香". 2022聚划算99划算节,聚划算首发「真香系列」,为消费者精选了一批价格香.品质香.服务香的「十三香」好物.不过,聚划算的" ...

  8. Android 接口的default 方法运行时报错AbstractMethodError

    [问题描述:接口default方法AbstractMethodError] 记录一个Android项目中遇到的问题,我们通过exclude方式重写了一个依赖,改用本地的实现,其中一个接口的defaul ...

  9. 真香系列之 Golang 升级

    Golang 以前的依赖管理一直饱受诟病,社区的方案也层出不穷,比如 vendor, glide, godep 等.之前的依赖管理一直是依靠 GOPATH 或者将依赖代码下载到本地,这种方式都有劣势. ...

最新文章

  1. 39个超实用jQuery实例应用特效
  2. 12种主流编程语言输出“ Hello World ”,把我给难住了!
  3. flex布局常用属性
  4. CSS实现强制换行-------Day 78
  5. 【 随笔 】 财源滚滚
  6. collections中的OrderedDict
  7. linux 的date命令详解,linux之date命令详解
  8. Linux 基础知识(2)---Linux内核空间内存申请函数kmalloc、kzalloc、vmalloc的区别
  9. Pycharm连接BitBucket
  10. Python内置数据结构——字符串string
  11. Android集成百度地图
  12. 管理新语:根据工作难度,及时调整负责人员
  13. 实战演习(四)——网络流量系统分析简介
  14. DTIM间隔在组播和节电方面的综合考虑
  15. 运维简历怎么写项目描述_职场小白怎么写简历?一份好简历=成功一半
  16. 微分,不定积分,定积分的简单理解
  17. php 用gd库在图片上写文字,并处理文字糊模问题
  18. 魔法密文 Writeup| DASCTF Oct X 吉林工师
  19. 两天碾转两个学校办卡地推
  20. GitBook在Windows下安装部署

热门文章

  1. Emacs 中英文字体设置
  2. 复制加网站信息的javascript代码及对应的javascript阻止命令
  3. ZOJ 1094 带括号的矩阵连乘
  4. 概率论与数理统计 (二)计算题和应用题
  5. linux中的环境变量作用
  6. SQL函数学习(函数在实际项目中 之 COALESCE)
  7. 【MySQL】RPM包安装
  8. 解决bootstrap dropdown 下拉菜单有时候不能显示的问题
  9. Android MVP Presenter 中引发的空指针异常
  10. 大数据学习系列之八----- Hadoop、Spark、HBase、Hive搭建环境遇到的错误以及解决方法