用强数据类型保护你的表单数据-基于antd表单的类型约束 | 京东云技术团队

京东云开发者
• 阅读 152

概述

接口数据类型与表单提交数据类型,在大多数情况下,大部分属性的类型是相同的,但很少能做到完全统一。

我在之前的工作中经常为了方便,直接将接口数据类型复用为表单内数据类型,在遇到属性类型不一致的情况时会使用any强制忽略类型错误。

后来经过自省与思考,这种工作模式会引起各种隐藏bug,一定有更好的工程解决方案。

我的答案就是:为表单提交数据单独定义类型!

类型解说

接口定义的请求体类型

请求数据类型

type RequestBody = {
   name?: string
   count?: number
   groupIds?: number[]
   startDate?: string // YYYY-MM-DD
}

表单提交数据类型定义

type FormValue = {
  name?: string
  count?: number
  groupIds?: string
  startDate?: Moment
}

有了该类型,我们可以方便的将该类型使用在表单实例上

const [form] = Form.useForm< FormValue >()

类型复用优化

如果表单的数据类型和接口提交的数据类型完全一致,当然可以共用一个,但现实世界这种情况几乎没有。

大多数情况是可以复用一些接口的属性到表单的数据类型中,例如上面的两个数据结构,其中 name、id 属性是相同的,则FormValue 可以优化为

type FormValue = Pick< RequestBody, 'name' | 'count' > {
  groupIds?: string
  startDate?: Moment
}

Form.Item 限定 name 优化

应用此时表单组件的name约束就应为我们自定义的表单数据类型FormValue,定义约束组件

const FormItem = Form.Item as React.FC<
  Omit< FormItemProps, 'name' > & {
    name: keyof FormValue
  }
>

应用该约束组件

< FormItem label="名称" name="name" > ...

数据转换

在表单的onFinish提交过程中,需要一个将FormValue(表单数据)转换为RequestBody(提交数据)的函数,类型定义如下:

const formValueToRquestBody = (values: FormValue): RequestBody => {
  return {
    name: values.name,
    id: values.id,
    groupIds: values.groupIds.split(',').map(n => Number(n)),
    startDate: values.startDate?.format('YYYY-MM-DD'),
  }
}

复杂表单类型

复杂表单有些表单数据并非一层的key => value,而是多层树状或数组结构。

例如:提交数据结构

type FormValue = {
  name: string
  rule: {
    min: number
    max: number
  }
}

表单中关于rule 的写法为:

< Form.Item name={['rule', 'min']}>

这种情况下,name不再是简单的字符串,应该如何用类型约束?

如果可以我希望使用类型工具,兼容多层表单数据结构,但一直没成功。

我目前的方法是,为该表单层级安排一个专用类型,该方法会有些写的麻烦,但胜在能准确的定义好类型。

我在采用该方法校验表单name数据时发现了几个很难发现的拼写错误,提前制止了测试同学提bug过来。

例如为rule属性定义单独的RuleFormItem

import type { FormItemProps } from 'antd'

const RuleFormItem = Form.Item as React.FC<
  Omit< FormItemProps, 'name'> & {
    name: ['rule', keyof FormValue['rule']]
  }
>

调用时

< RuleFormItem label="min" name={['rule', 'min']}> ...

此时数组中的 rule 与 min 都能收到类型的保护。

泛型抽象

export type TypedFormItem< T > = React.FC<
  Omit< FormItemProps, 'name' > & {
    name: T
  }
>

应用泛型

const RuleFormItem = Form.Item as TypedFormItem< keyof FormValue >

🎉🎊 恭喜,现在你的表单已经被类型完整的保护了。

作者:京东零售 王凡

来源:京东有开发者社区 转载请注明来源

点赞
收藏
评论区
推荐文章
LinMeng LinMeng
3年前
js之传值与传址/undefined和null/严格模式
传值与传址基本数据类型有五种Undefined、Null、Boolean、Number和String引用数据类型有两种object,array,fn两种数据类型的区别:1.存储位置不同原始数据类型直接存储在栈(stack)中简单数据段,占据空间小,大小固定,属于被频繁使用的数据,所以存储在栈中;引用数据类型直接存
Wesley13 Wesley13
2年前
SASS基础
特点:1.在css基础上增加特性:变量、嵌套(nesting)、混合(@mixin)、继承(@extend);2.通过函数进行颜色值与属性值的运算;3.提供控制指令等高级功能;4.自定义输出指令;数据类型:Sass和JavaScript语言类似,也具有自己的数据类型,在Sass中包含以下几种数据类型:1\
Wesley13 Wesley13
2年前
Java对象的浅拷贝和深拷贝&&String类型的赋值
Java中的数据类型分为基本数据类型和引用数据类型。对于这两种数据类型,在进行赋值操作、方法传参或返回值时,会有值传递和引用(地址)传递的差别。浅拷贝(ShallowCopy):①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,
Stella981 Stella981
2年前
JavaScript的深拷贝和浅拷贝
一、数据类型数据分为基本数据类型(String,Number,Boolean,Null,Undefined,Symbol)和对象数据类型。、1.基本数据类型的特点:直接存储在栈(stack)中的数据2.引用数据类型的特点:存储的是该对象在栈中引用,真实的数据放在堆内存里。引用数据类型在栈中存储了指针,该指针指向堆中该实
Wesley13 Wesley13
2年前
Java中的基本数据类型和引用数据类型的区别
一、前言众所周知Java是一种强类型语言,在Java语言中,Java的数据类型一共分为两大类,分别为基本数据类型和引用数据类型,其中基本数据类型细分小类可分为整数类型、浮点类型、字符类型、布尔类型这四小类。二、基本数据类型和引用数据类型1\.基本数据类型只有
可莉 可莉
2年前
06_java之类概述
01引用数据类型\_类A:数据类型a:java中的数据类型分为:基本类型和引用类型B:引用类型的分类a:Java为我们提供好的类,比如说:Scanner,Random等。b:我们自己创建的类,按照类的定义标准,可以在类中包含多个方法与属性,来供我
Wesley13 Wesley13
2年前
Java int与Integer的区别
int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别:int是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象1.Java中的数据类型分为基本数据类型和复杂数据类型int是前者而integer是后者(也就是一个类);因此在类进行初始化时int类的变量初始为0.而Integer的变量则初始化为n
Easter79 Easter79
2年前
Struts2 转换器
转换器从一个HTML表单到一个Action对象,类型转换是从字符串到非字符串Http没有“类型”的概念,每一项表单的输入只可能是一个字符串或一个字符串数组,在服务器端必须把String转换为特定的数据类型在struts2中把请求参数映射到action属性的工作由Paramet
Stella981 Stella981
2年前
Js——第一课
数据类型:要特别注意相等运算符。JavaScript在设计时,有两种比较运算符:第一种是比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;第二种是比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。由于JavaScript这个设计缺陷,_不要_使用比较,始终
Wesley13 Wesley13
2年前
MySQL数据完整性
数据完整性一个数据库就是一个完整的业务单元,可以包含多张表,数据被存储在表中在表中为了更加准确的存储数据,保证数据的正确有效,可以在创建表的时候,为表添加一些强制性的验证,包括数据字段的类型、约束数据类型可以通过查看帮助文档查阅所有支持的数据类型使用数据类型的原则是:够用就行,尽量使用取值