文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Typescript怎么使用装饰器实现接口字段映射与Mock

2023-07-06 04:29

关注

本篇内容主要讲解“Typescript怎么使用装饰器实现接口字段映射与Mock”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Typescript怎么使用装饰器实现接口字段映射与Mock”吧!

需求

最核心的问题就是要达到:接口字段的修改不能影响项目中实际使用的字段,无论是字段名的修改还是类型的修改

这里考虑使用装饰器附带额外信息,主要是接口字段信息,与需要转换的类型

既然可以转换类型了,考虑把字段 “翻译” 功能加上

既然能转换了,能就再加个 Mock 吧,摆脱开发过程中对后端接口的依赖

设计

语言:typescript 构建工具:rollup 自动化测试:jest 代码规范:eslint + prettier 提交规范:commitlint

Decorator

首先,我们需要一个对象

是这个对象 {}

class Lesson {  public name: string;  public teacher: string;  public datetime: string;  public applicants: number;  public compulsory: boolean;  constructor() {    this.name = "";    this.teacher = "";    this.datetime = "";    this.compulsory = false;  }}

上面的代码,就是我们构造出的 Lesson 类,它的属性字段就是我们会在项目中实际使用的字段

现在我们需要把这个类的属性字段与接口返回的字段对应上,这时候就需要用到 装饰器 了,随便取个名字,我这里是用 mapperProperty,接收两个参数,第一个是接口返回的字段名,第二个是期望最终得到的类型(不是接口字段本身的类型)

class Lesson {  @mapperProperty("ClassName", "string")  public name: string;  @mapperProperty("TeacherName", "string")  public teacher: string;  @mapperProperty("DateTime", "datetime")  public datetime: string;  @mapperProperty("ApplicantNumber", "int")  public applicants: number;  @mapperProperty("Compulsory", "boolean")  public compulsory: boolean;  constructor() {    this.name = "";    this.teacher = "";    this.datetime = "";    this.date = "";    this.time = "";    this.compulsory = false;  }}

如上面的代码,我们给每个属性字段都加上了装饰器,并告知了接口中对应的字段名称,以及我们希望得到的类型。 例如代码中的 applicants 字段,对应了接口中的 ApplicantNumber 字段,无论接口返回的是字符串还是数值类型,我们都希望最终得到的是 int 类型(指代整数)的数据

接下来要把接口字段名称与我们期望得到的类型先缓存起来

这里我们借助 Reflect Metadata 实现缓存

示例代码如下

function mapperProperty(apiField, type) {  Reflect.metadata("key", {    apiField, // 接口字段名    type, // 期望类型  });}

Reflect Metadata 是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据;我们使用 reflect-metadata 来模拟该功能

Transform

有了接口字段名与期望的类型,接下来的转换就简单了

第一步,先读取上一步缓存的元数据信息

const instance = new Lesson();const meta = Reflect.getMetadata("key", instance, "applicants");console.log(meta);

这里的 key 即元数据的键,上面的代码是读取 Lesson 类中 applicants 字段的元数据,meta 打印的结果如下

{    apiField: 'ApplicantNumber',    type: 'int'}

第二步,转换

function deserialize(clazz, json) {  const instance = new clazz();  const meta = Reflect.getMetadata("key", instance, "applicants");  const { apiField, type } = meta;  const ori = json[apiField]; // json 为接口返回的数据  let value;  switch (type) {    case "int":      value = parseInt(ori, 10);      break;    // 其它类型转换  }  // 后续处理}

到这基本就实现了最核心的能力,只要愿意可以扩展更多类型,欢迎一起来完善

Object and Array

对象与数组的转换与基本类型的转换大差不差,这里我将对象、数组的装饰器命名为 deepMapperProperty,只需将第二个参数的类型,改为接收一个类即可

示例代码如下

function deepMapperProperty(apiField, clazz) {  Reflect.metadata("key", {    apiField, // 接口字段名    clazz, // 子级  });}

取值方式同上,不再赘述了,只需改一下转换的代码

转换对象的示例代码如下,递归调用一下即可

const { clazz } = meta;if (clazz) {  value = deserialize(clazz, value);}

数组则直接使用 map 遍历

function deserializeArr(clazz, list) {  return list.map((ele) => deserialize(clazz, ele));}

Mock

模拟数据部分,是直接返回的前端项目中使用的字段,而非修改接口字段的返回值

实现模拟数据拢共分三步:

与转换同样的步骤,要先读取字段的期望类型,这里只需要类型即可

遍历读取类中各个字段的元数据,得到各个字段的期望类型

根据期望类型使用不同的随机函数,生成相应类型的数据,这里我封装了三种类型的随机函数

针对对象与数组特殊处理

使用

安装

npm i type-json-mapper

属性装饰器

内置三种类属性装饰器:

@mapperProperty(apiField, type)

基本数据类型使用该装饰器

接收两个参数:

@deepMapperProperty (apiField, Class)

对象/数组使用该装饰器

接收两个参数:

@filterMapperProperty(apiField, filterFunc)

自定义过滤器(翻译)使用该装饰器

接收两个参数:

const filterFunc = (value) => {  return "translated text";};

方法

deserialize(Clazz, json)

反序列化 json 对象

deserializeArr(Clazz, list)

反序列化数组

mock(Clazz, option)

生成模拟数据

mock 配置

名称类型描述默认值
fieldLengthObject字段长度-
arrayFieldsstring[]数组类型字段-

fieldLength

数据类型length 含义
string字符串长度
int最大整数
float字符长度(保留两位小数)

例:

class Student {  @mapperProperty("StudentID", "string")  public id: string;  @mapperProperty("StudentName", "string")  public name: string;  @mapperProperty("StudentAge", "int")  public age: number;  @mapperProperty("Grade", "float")  public grade: number;  constructor() {    this.id = "";    this.name = "";    this.age = 0;    this.grade = 0;  }}mock(Student, { fieldLength: { age: 20, grade: 4, name: 6 } });

使用示例

这里预先造了几个类,并给类属性加上了装饰器

import {  mapperProperty,  deepMapperProperty,  filterMapperProperty,} from "type-json-mapper";class Lesson {  @mapperProperty("ClassName", "string")  public name: string;  @mapperProperty("Teacher", "string")  public teacher: string;  @mapperProperty("DateTime", "datetime")  public datetime: string;  @mapperProperty("Date", "date")  public date: string;  @mapperProperty("Time", "time")  public time: string;  @mapperProperty("Compulsory", "boolean")  public compulsory: boolean;  constructor() {    this.name = "";    this.teacher = "";    this.datetime = "";    this.date = "";    this.time = "";    this.compulsory = false;  }}class Address {  @mapperProperty("province", "string")  public province: string;  @mapperProperty("city", "string")  public city: string;  @mapperProperty("full_address", "string")  public fullAddress: string;  constructor() {    this.province = "";    this.city = "";    this.fullAddress = "";  }}// 状态映射关系const stateMap = { "1": "读书中", "2": "辍学", "3": "毕业" };class Student {  @mapperProperty("StudentID", "string")  public id: string;  @mapperProperty("StudentName", "string")  public name: string;  @mapperProperty("StudentAge", "int")  public age: number;  @mapperProperty("StudentSex", "string")  public sex: string;  @mapperProperty("Grade", "float")  public grade: number;  @deepMapperProperty("Address", Address)  public address?: Address;  @deepMapperProperty("Lessons", Lesson)  public lessons?: Lesson[];  @filterMapperProperty("State", (val: number) => stateMap[`${val}`])  public status: string;  @filterMapperProperty("Position", (val: number) => stateMap[`${val}`])  public position: string;  public extra: string;  constructor() {    this.id = "";    this.name = "";    this.age = 0;    this.sex = "";    this.grade = 0;    this.address = undefined;    this.lessons = undefined;    this.status = "";    this.position = "";    this.extra = "";  }}

以下是接口返回的数据:

const json = [  {    StudentID: "123456",    StudentName: "李子明",    StudentAge: "10",    StudentSex: 1,    Grade: "98.6",    Address: {      province: "广东",      city: "深圳",      full_address: "xxx小学三年二班",    },    Lessons: [      {        ClassName: "中国上下五千年",        Teacher: "建国老师",        DateTime: 1609430399000,        Date: 1609430399000,        Time: 1609430399000,        Compulsory: 1,      },      {        ClassName: "古筝的魅力",        Teacher: "美丽老师",        DateTime: "",      },    ],    State: 1,    Position: 123,    extra: "额外信息",  },  {    StudentID: "888888",    StudentName: "丁仪",    StudentAge: "18",    StudentSex: 2,    Grade: null,    Address: {      province: "浙江",      city: "杭州",      full_address: "xxx中学高三二班",    },    Lessons: [],    State: 2,  },];

开始转换,因接口返回的是数组,这里使用 deserializeArr

import { deserializeArr } from "type-json-mapper";try {  const [first, second] = deserializeArr(Student, json);  console.log(first);  console.log(second);} catch (err) {  console.error(err);}

输出结果如下

// first
{
  id: "123456",
  name: "李子明",
  age: 10,
  sex: "1",
  grade: 98.6,
  address: { province: "广东", city: "深圳", fullAddress: "xxx小学三 年二班" },
  lessons: [
    {
      name: "中国上下五千年",
      teacher: "建国老师",
      datetime: "2020-12-31 23:59:59",
      date: "2020-12-31",
      time: "23:59:59",
      compulsory: true,
    },
    {
      name: "古筝的魅力",
      teacher: "美丽老师",
      datetime: "",
      date: undefined,
      time: undefined,
      compulsory: undefined,
    },
  ],
  status: "读书中",
  position: 123,
  extra: "额外信息",
};
// second
{
  id: "888888",
  name: "丁仪",
  age: 18,
  sex: "2",
  grade: null,
  address: { province: "浙江", city: "杭州", fullAddress: "xxx中学高三二班" },
  lessons: [],
  status: "辍学",
  position: undefined,
  extra: undefined,
};

如果后端接口还没开发完成,我们还可以直接 mock

import { mock } from "type-json-mapper";const res = mock(Student, {  fieldLength: { age: 20, grade: 4, name: 6 },  arrayFields: ["lessons"],});console.log(res);

输出结果如下

{
  id: 'QGBLBA',  name: 'KTFH6d',
  age: 4,
  sex: 'IINfTm',
  grade: 76.15,
  address: { province: 'qvbCte', city: 'DbHfFZ', fullAddress: 'BQ4uIL' },
  lessons: [
    {
      name: 'JDtNMx',
      teacher: 'AeI6hB',
      datetime: '2023-2-18 15:00:07',
      date: '2023-2-18',
      time: '15:00:07',
      compulsory: true
    },
    {
      name: 'BIggA8',
      teacher: '8byaId',
      datetime: '2023-2-18 15:00:07',
      date: '2023-2-18',
      time: '15:00:07',
      compulsory: false
    },
    {
      name: 'pVda1n',
      teacher: 'BPCmwa',
      datetime: '2023-2-18 15:00:07',
      date: '2023-2-18',
      time: '15:00:07',
      compulsory: false
    }
  ],
  status: '',
  position: '',
  extra: ''
}

到此,相信大家对“Typescript怎么使用装饰器实现接口字段映射与Mock”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯