TypeScript 真香系列——接口篇

接口带来了什么好处

好处One —— 过去咱们写 JavaScript

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

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

函数调用:前端

getUserInfo({name: "koala", age: 18})
复制代码

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

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

JavaScript 是弱类型的语言,因此并不会对咱们传入的代码进行任何的检测,有些错你本身都说不清楚,可是就出了问题。git

TypeScript 中的 interface 能够解决这个问题

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

正确的调用是以下的方式:程序员

getUserInfo({name: "kaola", age: 18});
复制代码

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

// 错误的调用
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 的必要性。typescript

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

咱们先定义一个 IUser 接口:编程

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

接下来咱们看一下函数如何来写:c#

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`参数
 }
// - 商品
export interface IGoodsQuery extends Query {
    isOnline?: string | number; // 是否出售中的商品
    goodsNo?: string; // 商品编号
    goodsName?: string; // 商品名称
 }
复制代码

好的,说了他的几个好处以后,咱们开始学习interface知识吧!还有不少优势哦!

做者简介:koala,专一完整的 Node.js 技术栈分享,从 JavaScript 到 Node.js,再到后端数据库,祝您成为优秀的高级 Node.js 工程师。【程序员成长指北】做者,Github 博客开源项目 github.com/koala-codin…

接口的基础篇

接口的定义

和 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); // undefined
p.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<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 不行

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<Entity> = {
    list: Entity[];
    total: number;
    [propName: string]: any;
};

// - 商品
export interface GoodsQuery extends Query {
    isOnline?: string | number; // 是否出售中的商品
    goodsNo?: string; // 商品编号
    goodsName?: string; // 商品名称
}
export type GoodResult = QueryResult<Good>;

复制代码

总结

TypeScript 仍是挺香的,预告一篇明天的发文吧,TypeScript强大的类型别名。今天就分享这么多,若是对分享的内容感兴趣,能够关注公众号「程序员成长指北」,加我微信(coder_qi),拉你进技术群,长期交流学习。

参考文章

juejin.im/post/5c8fbf…

www.teakki.com/p/57dfb5a0d…

juejin.im/post/5c2723…

mp.weixin.qq.com/s/aj45tr7AZ…

cw.hubwiz.com/card/c/55b7…

Node系列原创文章

深刻理解Node.js 中的进程与线程

想学Node.js,stream先有必要搞清楚

require时,exports和module.exports的区别你真的懂吗

源码解读一文完全搞懂Events模块

Node.js 高级进阶之 fs 文件模块学习

关注我

  • 欢迎加我微信(coder_qi),拉你进技术群,长期交流学习...
  • 欢迎关注「程序员成长指北」,一个用心帮助你成长的公众号...
相关文章
相关标签/搜索