yup 使用 2 - 获取默认值,循环依赖,超大数字验证,本地化

yup 使用 2 - 获取默认值,循环依赖,超大数字验证,本地化

上一篇的使用在这里:yup 基础使用以及 jest 测试,这篇讲的是比较基础的东西,

获取默认值

之前用的都是 cast({}),然后如果有些值是必须的,又没有提供默认值,yup 就会抛出异常。另一种可以直接获取默认值而不抛出异常的方式,可以使用内置的 getDefault()

const res = demoSchema.getDefault();

类型异常

如果使用 JavaScript 应该没什么问题,但是如果使用 TypeScript 的话,可能会抛出如下的异常:

在这里插入图片描述

这是因为导出的数据类型使用的是 Infer,schema 中没有定义默认值,因此就会出现 undefined 和字符串不匹配的情况,这种解决的方式可以通过重写 default 来实现:

export let demoSchema = object({// ...enumField: string().required().default(() => undefined as undefined | string).oneOf(Object.keys(getTestEnum() || [])),
});

大多数情况下这应该不会有什么问题,只有在直接获取默认值并需要重新赋值的时候需要注意

循环依赖

这也是我们项目里存在的一个比较罕见的案例,就是需要同时检查 A 和 B

正常情况下,如果直接使用下面的实现,则会抛出异常:

export let demoSchema = object({// ...dependentField1: number().required().when("dependentField2", ([dependentField2], schema) => {return schema;}),dependentField2: number().required().when("dependentField1", ([dependentField1], schema) => {return schema;}),
});

在这里插入图片描述

造成这个错误的原因是,当 dependentField2 需要验证的时候,它需要去找 dependentField1 的值,而 dependentField1 又需要对 dependentField2 进行判断……

解决的方式可以使用 shape(),并且将依赖作为 dependency array 放到第二个参数中:

export let demoSchema = object().shape({// ...dependentField1: number().default(0).required().when("dependentField2", ([dependentField2], schema) => {return schema;}),dependentField2: number().default(0).required().when("dependentField1", ([dependentField1], schema) => {return schema;}),},[["dependentField1", "dependentField2"]]
);

具体实现的验证为:

number().default(0).required().when("dependentField2", ([dependentField2], schema) => {return schema.test({name: "none-zero",test: (dependentField1) =>!(dependentField1 === 0 && dependentField2 === 0),message: "DependentField1 and DependentField2 cannot be both 0.",});});

这就会检查 dependentField1dependentField2 是否同时为 0:

在这里插入图片描述

⚠️:在查文档的时候,我看到的目前 yup 支持是两两相对的,因此 dep array 中只能放 [[a, b], [b, c], [c, a]] 这样的实现,而不能使用 [[a, b, c]] 这样的实现

👀:我看到一些其他、实际的使用案例为地址,如一旦输入了地址,那么就需要验证城市、省/直辖区和邮政编码,如果没有输入地址,则不需要进行验证

数字最大值问题

这个实际上不是 yup 的问题,而是 JavaScript 的问题,如 JS 中有一个 MAX_SAFE_INTEGER 值,并且进行转换后,就会失去精确值:

在这里插入图片描述

而且对于 JS 本身来说,任何超过 MAX_SAFE_INTEGER 的操作,都会导致丢失精确值,而这里的挑战是:

  • 一旦使用任何的数字,并且保存到 JavaScript 中,就像使用 parseFloat 这个案例,精确值直接就丢了
  • 如果使用 number,在调用 yup 的时候,yup 内部会使用类似 parseFloat 的实现,因此也会导致精度丢失

因为后端暂时还是只接受 decimal/long,所以我们目前的解决方式是用 decimal.js,这个库的实现方式类似于 Java 的 big decimal,所以只要不转成浮点数,而是用字符串,就能够保持我们项目需要的精度

util 实现

主要是重写一些 Decimal 内部的比较方法,因为 Decimal 不接受 undefined,所以不写 util 会导致报错

import Decimal from "decimal.js";export const compareTo = (a: Decimal.Value | undefined,b: Decimal.Value | undefined
): number => new Decimal(a ?? 0).comparedTo(b ?? 0);export const equalTo = (a: Decimal.Value | undefined,b: Decimal.Value | undefined
): boolean => new Decimal(a ?? 0).equals(b ?? 0);

更新 yup 验证

因为精确的关系,所以就需要把所有的数字转成字符串,并且手动重写验证,大体实现如下:

const MAX_DECIMAL = new Decimal(Number.MAX_SAFE_INTEGER).div(10 ** 6);export let demoSchema = object().shape({// ...dependentField1: string<string>().default(() => "0" as string).required().test({name: "max-num",test: (dependentField1) => {return compareTo(dependentField1, MAX_DECIMAL) <= 0;},message: `DependentField1 must be smaller than or equal to ${MAX_DECIMAL}.`,}),},[["dependentField1", "dependentField2"]]
);

最后的验证如下:

在这里插入图片描述

这里对象的值为:

const res = demoSchema.getDefault();
res.dependentField1 = "9007199254.740991";
res.dependentField2 = "9007199254.740992";demoSchema.validate(res, { abortEarly: false }).then((validatedRes) => console.log(validatedRes)).catch((e: ValidationError) => {e.inner.forEach((e) => {console.log(e.path, e.errors);});});

⚠️:max-num 的这个对象是可以改成一个函数,这样可以稍微减少一些代码:

export const maxNumTest = (fieldName: string,maxValue: number | Decimal.Value
) => ({name: "max-num",test: (value: any) => {return compareTo(value, maxValue) <= 0;},message: `${fieldName} must be smaller than or equal to ${maxValue}.`,
});

补充 - 精确值计算

这里主要就是 stack overflow 的解决方案:How can I deal with floating point number precision in JavaScript

大致运行是这样:

> var x = 0.1
> var y = 0.2
> var cf = 10
> x * y
0.020000000000000004
> (x * cf) * (y * cf) / (cf * cf)
0.02

里面提出的解决方式是:

var _cf = (function () {function _shift(x) {var parts = x.toString().split(".");return parts.length < 2 ? 1 : Math.pow(10, parts[1].length);}return function () {return Array.prototype.reduce.call(arguments,function (prev, next) {return prev === undefined || next === undefined? undefined: Math.max(prev, _shift(next));},-Infinity);};
})();Math.a = function () {var f = _cf.apply(null, arguments);if (f === undefined) return undefined;function cb(x, y, i, o) {return x + f * y;}return Array.prototype.reduce.call(arguments, cb, 0) / f;
};Math.s = function (l, r) {var f = _cf(l, r);return (l * f - r * f) / f;
};Math.m = function () {var f = _cf.apply(null, arguments);function cb(x, y, i, o) {return (x * f * (y * f)) / (f * f);}return Array.prototype.reduce.call(arguments, cb, 1);
};Math.d = function (l, r) {var f = _cf(l, r);return (l * f) / (r * f);
};

我们内部使用的也是这个方式去计算还原,目前对于还原到 MAX_SAFE_INTEGER 来说问题不大……

setLocale

这是一个本地可以解决一些报错信息的方式,目前我找到的是内嵌的方法,如 required 这种,大致实现方式如下:

// 写在了另一个 const 文件里
export const FIELD_NAME: Record<string, string> = {description: "Description",enumField: "Dropdown Enum",
};// 在 schema util 里的实现……或许放到 const 或者 i18 也行
setLocale({mixed: {required: ({ path }) => `${FIELD_NAME[path]} is a required field.`,oneOf: ({ path, values }) =>`${FIELD_NAME[path]} must have one of the following fields: ${values}.`,},string: {min: ({ path, min }) =>`${FIELD_NAME[path]} must be at least ${min} characters.`,max: ({ path, max }) =>`${FIELD_NAME[path]} must be at at most ${max} characters.`,},
});

这个 setLocale 只需要实现一次,所有的 schema 就会沿用这个设定,如:

在这里插入图片描述

做 i8 是个比较方便的设置

我目前还没有找到特别好的能够解决 testwhen 里的报错信息,可能说最终只会写一些其他的函数用来解决这个问题吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/409836.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

叉车(工业车辆)安全管理系统,云端监管人车信息运营情况方案

近年来&#xff0c;国家和各地政府相继出台了多项政策法规&#xff0c;从政策层面推行叉车智慧监管&#xff0c;加大叉车安全监管力度。同时鼓励各地结合实际&#xff0c;积极探索智慧叉车建设&#xff0c;实现作业人员资格认证、车辆状态认证、安全操作提醒、行驶轨迹监控等&a…

如何利用电商 API 数据分析助力精准选品!

电商 API 数据分析在选品过程中起着至关重要的作用&#xff0c;它们之间有着密切的关系&#xff1a; 一、提供市场趋势洞察 热门商品识别&#xff1a; 通过分析电商 API 中的销售数据&#xff0c;包括商品的销售量、销售额、销售频率等指标&#xff0c;可以快速准确地识别出当…

1Panel应用推荐:MeterSphere开源持续测试工具

1Panel&#xff08;github.com/1Panel-dev/1Panel&#xff09;是一款现代化、开源的Linux服务器运维管理面板&#xff0c;它致力于通过开源的方式&#xff0c;帮助用户简化建站与运维管理流程。为了方便广大用户快捷安装部署相关软件应用&#xff0c;1Panel特别开通应用商店&am…

redis面试(二十一)读写锁互斥

读锁非互斥 非互斥的意思就是&#xff0c;一个客户端或者线程加锁之后&#xff0c;另一个客户端线程也可以来进行加锁。 还是拿着ReadLock的lua脚本来看看 刚才我们已经分析过第一个线程来加读锁的逻辑了 所以上半截不用重复说了&#xff0c; hset anyLock mode read hset an…

后端微服务架构:构建分布式博客系统

后端微服务架构&#xff1a;构建分布式博客系统 在当今的软件开发领域&#xff0c;微服务架构已经成为构建可扩展、灵活且易于维护的应用程序的主流选择。本文将探讨如何利用微服务架构来设计和实现一个分布式的博客系统。 1. 微服务架构简介 微服务架构是一种将应用程序分解…

【微服务部署】Linux部署微服务启动报ORA-01005

问题背景&#xff1a; Linux机器部署springboot微服务&#xff0c;部署完成后发现无法启动&#xff0c;后台报ORA-01005错误。 解决方案&#xff1a; 1.检查当前服务器是否已安装oracle客户端 命令行执行sqlplus username/passwd实例名&#xff0c;如果执行成功&#xff0c;说…

微信小程序源码 图书管理系统 万字文档 Springboot vue

源码地址 系统演示 SpringBoot vue 微信小程序源码 图书管理系统 附带运行教程 系统演示 万字文档&#xff0c;全套开发工具 开发工具:IDEA,微信小程序工具 数据库:mysql8 使用环境:Windows JDK版本:1.8 后端构建工具:maven 项目使用到的技术栈 Springboot2 mybatis vue Mys…

计算机毕业设计选题推荐-摇滚音乐鉴赏网站-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

领域驱动设计DDD详解与战术建模落地

一、什么是DDD&#xff1f; 1.1、DDD的概念 Domain-Driven Design&#xff08;领域驱动设计&#xff09;它由Eric Evans在他的2003年出版的书籍《Domain-Driven Design: Tackling Complexity in the Heart of Software》中首次提出。DDD 核心思想是通过领域驱动设计方法定义领…

透明度测试

&#xff11;、透明测试是用于处理哪种透明需求 在游戏开发中对象的某些部位完全透明而其他部位完全不透明,这种透明需求往往不需要半透明效果,相对比较极端&#xff0c;只有看得见和看不见之分比如树叶、草、栅栏等等。&#xff08;即一张图除了主要物体有颜色&#xff0c;其…

奇文网盘项目对应windows版本的中间件下载,otp,rabbitmq,postgresql,onlyoffice(在线预览编辑等)

之前的解压是百度网盘&#xff0c;要会员&#xff0c;油猴也无法下载&#xff0c;所有我下载之后给你们提供阿里云盘链接&#xff08;不限速&#xff09; 本次软件版本介绍&#xff1a; 01-otp_win64_24.1 02-rabbitmq-server-3.9.8 03-postgresql-9.6.23-2-windows-x64 0…

如何解决错误Given calling package android does not match caller‘s uid-学员提问

背景&#xff1a; 近来有学员反馈说wms课程中讲解的借壳Shell帮忙执行一些shell命令有问题&#xff0c;具体啥问题呢&#xff1f; 在ShellProvider的call方法加入如下代码&#xff1a; 目的就是想让shell帮我们执行一下settings值的写入&#xff0c;这里其实可以更加简单的set…

Linux 定时备份

背景&#xff1a;为防止数据丢失&#xff0c;要求每天备份一次 以达梦数据库为例&#xff0c;每天定时备份 1.填写备份脚本 vi db_day_backup.sh #!/bin/bash DIR$(cd $(dirname $0) && pwd) tarnamedata.tar_$(date %Y%m%d) cd $DIR if [[ $(find $DIR/ -name $tar…

腾讯提出一种新的针对风格化角色和逼真服装动画的生成3D运动转移方法,生成效果逼真!

来自腾讯XR视觉实验室的研究团队提出了一种创新的3D运动转移方法&#xff0c;专门针对风格化角色和逼真服装动画的生成。该方法能够将源动作准确地映射到目标角色上&#xff0c;同时考虑了角色身体的刚性变形和服装的局部物理动态变形。 与现有技术相比&#xff0c;这技术不仅…

4、Unity【基础】画线功能Linerenderer、物理系统Physics

文章目录 画线功能Linerenderer1、LineRenderer是什么2、LineRender参数相关3、LineRender代码相关思考1 请写一个方法&#xff0c;传入一个中心点&#xff0c;传入一个半径&#xff0c;用LineRender画个圆出来思考2 在Game窗口长按鼠标用LineRender画出鼠标移动的轨迹 核心系统…

Axure设计之单选框教程(中继器)

在Axure RP中&#xff0c;通过结合中继器的强大功能&#xff0c;我们可以轻松实现动态加载的单选框列表&#xff0c;不仅可以根据数据自动调整选项宽度&#xff0c;还能实时更新选中状态。本教程将引导你完成一个使用中继器制作动态单选框列表的项目&#xff0c;包括案例分析、…

Linux文件IO缓存

一、缓冲区大小对 I/O 系统调用性能的影响 总之&#xff0c;如果与文件发生大量的数据传输&#xff0c;通过采用大块空间缓冲数据&#xff0c;以及执行更少的 系统调用&#xff0c;可以极大地提高 I / O 性能 二、stdio 库的缓冲 当操作磁盘文件时&#xff0c;缓冲大块数据以…

树数据结构(Tree Data Structures)的全面指南:深度解析、算法实战与应用案例

树数据结构&#xff08;Tree Data Structures&#xff09;的全面指南&#xff1a;深度解析、算法实战与应用案例 引言 树数据结构&#xff08;Tree Data Structures&#xff09;作为计算机科学中的基石之一&#xff0c;以其独特的层次结构和分支特性&#xff0c;在众多领域发…

IIS中检测不到AspNetCoreModuleV2模块

安装了.net 2.2 的runtime&#xff08;运行时&#xff09;&#xff0c;但是在IIS中还是没有检测出来AspNetCoreModuleV2模块 解决方案&#xff1a; 其实问题点主要是选错了包&#xff0c;选成了x64&#xff0c;应该选择Hosting Bundle&#xff0c;这个是与IIS有关的。 之后下…

HW数通IA笔记2-网络参考模型

目录 零、本章主要内容 一、应用和数据 二、网络参考模型与标准协议 2.2 TCP/IP参考模型 2.3 TCP/IP常见协议 2.3.1 应用层 2.3.2 传输层 2.3.3 网络层 2.3.4 数据链路层 2.3.5 物理层 2.4 常见的协议标准化组织 三、数据的通信过程 零、本章主要内容 1、理解数据的…