最佳实践:如何实现函数参数之间的TS类型相依赖和自动推断
引入
最近在开发一款极致优雅的前端状态管理库AutoStore时碰到这样一个问题。
拟实现Field
组件,该组件相关类型简化代码如下:
type Field = (props:{validate,render:(props:{value,isValid})
})
该组件,具有validate
和render
两个属性:
- 其中
validate
是校验函数,可以同步校验函数或者异步校验函数 - 重点是
render
渲染函数的props.isValid
的类型,则是动态的,我们希望其依赖于字段的validate
属性。- 当
validate
是同步校验函数时,render.props.isValid=<boolean>
- 当
validate
是异步校验函数时,render.props.isValid={value:boolean,loading:boolean}
- 当
问题很明了,就是函数中的一个参数的类型依赖于另外一个参数的类型。
下面,我详细分析如何实现以及介绍踩过的坑,马上开始。
路坑实现过程
第1步:声明validate
校验参数的类型
首先,我们声明validate
校验参数的类型:
type ComputedGetter<Value=any> = ()=>Value
type AsyncComputedGetter<Value=any> = ()=>Promise<Value>
ComputedGetter
和AsyncComputedGetter
分别代码同步校验和异步校验函数。
第2步:根据不同校验方式转换返回值
接着,编写一个PickResult
类型,根据同步校验
和异步校验函数
返回不同的类型。
type PickResult<T> = T extends AsyncComputedGetter<infer X> ? {value:X,loading:boolean} : (T extends ComputedGetter<infer X> ? X : T)
如果是同步校验校验则返回boolean
,如果是异步校验就返回{value:X,loading:boolean}
第3步:编写Field
组件
然后编写Field
组件声明
function Field<Value = string,Validate extends ComputedGetter<boolean> | AsyncComputedGetter<boolean> = ComputedGetter<boolean> | AsyncComputedGetter<boolean>
>(props:{validate:Validate,render:(props:{value:Value,isValid: PickResult<Validate>})=>React.ReactNode}
){return <>{props.render({value:"AutoStore" } as any) }</>
}
Field
组件提供了两个Value
和Validate
两个泛型参数validate
属性可以是同步或异步校验函数- 重点:
render
属性的props.isValid
是由Validate
泛型参数决定的。
第4步:渲染Field
组件
最后让我们来使用渲染Field
组件
- **同步校验参数时 **
<Field validate = {()=>true}render={({value,isValid}) =>{return <>{ isValid ? <span>{value}</span> : <span>No</span>}</>}}/></>
以上validate = {()=>true}
,所以isValid
被自动推导为boolean
- **异步校验参数时 **
()=>{return <><Field validate = {async ()=>true}render={({value,isValid}) =>{return <>{ isValid.value ? <span>{value}</span> : <span>No</span>}</>}}/></>}
以上validate = {async ()=>true}
,所以isValid
被自动推导为{value:boolean,loading:boolean}
第5步:开始踩坑
以上我们编写的组件,已经实现了能根据validate
的属性输入来自动推断render.props
的类型。
render.props.isValid
的类型是根据validate
动态推断而来的。
看起来很完美是不是?
别急,让我们为泛型value
指定一个类型。
()=>{<><Field<number> validate = {()=>true}render={({value,isValid}) =>{return <>{ isValid ? <span>{value}</span> : <span>No</span>}</>}}/></>
}
- 以上我们为
value
指定了泛型number
,然后我们马上就发现isValid
被推断为:
boolean | { value: boolean; loading: boolean;
}
自动推断不生效了? 为什么会这样?
所以,如果我们不指定任何泛型参数,则自动推断生效,如果指定则失效。
问题就在这里:
在Typescript
里面,当不指定任何泛型参数时,Validate
是根据输入自动推断的,而一旦指定了任何泛型参数,则泛型匹配就开始,而以上我们为Validate
指定了一个默认值。
所以,Typescript
就按指定的默认值来为Validate
赋值,而不是根据动态输入,就相当于自动推断失效了.
那么如果我们不为validate
指定默认类型行不行呢?当然可以,但是显得很烦琐。
第6步:解决方案
我们想实现的是:
- 为
validate
指定类型约束 - 能根据动态输入自动推断
显然,上述方案存在问题,并不理想。
问题的核心在于Typescript
对是否指定泛型参数时的自动推规则
- 当没有指定泛形参数时,会进行自动推断。
- 当指定了泛形参数时, 则根据使用泛型参数声明,不进行动态的自动推断
知道了此规则,我们就有了如下的解决方案。
使用函数重载来解决此问题
function Field<Value = string,Validate extends ComputedGetter<boolean> = ComputedGetter<boolean>>(props:{validate:Validate,render:(props:{value:Value,isValid: PickResult<Validate>})=>React.ReactNode}):any
function Field<Value = string,Validate extends AsyncComputedGetter<boolean> = AsyncComputedGetter<boolean>>(props:{validate:Validate,render:(props:{value:Value,isValid: PickResult<Validate>})=>React.ReactNode}):any
function Field<Value,Validate>(props:{validate:Validate,render:(props:{value:Value,isValid: PickResult<Validate>})=>React.ReactNode}):any{return <>{props.render({value:"AutoStore" } as any) }</>
}
小结
一开始使用Validate extends ComputedGetter<boolean> | AsyncComputedGetter<boolean> = ComputedGetter<boolean> | AsyncComputedGetter<boolean>
来约束的validate
的本意是:
- 为
Validate
指定ComputedGetter<boolean> | AsyncComputedGetter<boolean>
约束 - 然后可以根据组件的
validate
参数来自动推断,让其他属性也可以自动推断。
但是由于Typescript
对是否指定泛型参数时的自动推规则的问题,需要采用函数重载方式才可以实现此功能。
至此,我们完美地实现以上功能。
顺推一下,AutoStore是新进出炉的一款响应式状态管理库,设计精良,功能强大,大家可以看看。
开源推荐
以下是我的一大波开源项目推荐:
- 全流程一健化React/Vue/Nodejs国际化方案 - VoerkaI18n
- 极致优雅的状态管理库 - AutoStore
- 无以伦比的React表单开发库 - speedform
- 终端界面开发增强库 - Logsets
- 简单的日志输出库 - VoerkaLogger
- 装饰器开发 - FlexDecorators
- 有限状态机库 - FlexState
- 通用函数工具库 - FlexTools
- 小巧优雅的CSS-IN-JS库 - Styledfc
- 为JSON文件添加注释的VSCODE插件 - json_comments_extension
- 开发交互式命令行程序库 - mixed-cli
- 强大的字符串插值变量处理工具库 - flexvars
- 前端link调试辅助工具 - yald
- 异步信号 - asyncsignal
- React/Vue/WebComponent树组件 - LiteTree
相关文章:
最佳实践:如何实现函数参数之间的TS类型相依赖和自动推断
引入 最近在开发一款极致优雅的前端状态管理库AutoStore时碰到这样一个问题。 拟实现Field组件,该组件相关类型简化代码如下: type Field (props:{validate,render:(props:{value,isValid}) })该组件,具有validate和render两个属性: 其中…...

Linux基础指令1
好久没写博客了,这次我将重新做人,每星期都更,做不到的话直接倒立洗头。最近在学Linux,感觉很厉害的样子,先浅学一下再弄数据结构去。 Linux的基本操作是通过指令来执行的,所以我们先来学习下指令。 1.简…...

软件设计师:排序算法总结
一、直接插入 排序方式:从第一个数开始,拿两个数比较,把后面一位跟前面的数比较,把较小的数放在前面一位 二、希尔 排序方式:按“增量序列(步长)”分组比较,组内元素比较交换 假设…...

「Mac畅玩鸿蒙与硬件25」UI互动应用篇2 - 计时器应用实现
本篇将带领你实现一个实用的计时器应用,用户可以启动、暂停或重置计时器。该项目将涉及时间控制、状态管理以及按钮交互,是掌握鸿蒙应用开发的重要步骤。 关键词 UI互动应用时间控制状态管理用户交互 一、功能说明 在这个计时器应用中,用户…...
计算机专业开题报告写法,该怎么写好?
不会写开题报告,或者想要一些论文模版的,欢迎评论,会第一时间给大家。 题报告是计算机专业大学毕业生在开展毕业设计或论文研究前,对研究课题进行详细介绍和计划的重要环节。作为开题者对科研课题的一种文字说明,开题…...

Vue(JavaScript)读取csv表格并求某一列之和(大浮点数处理: decimal.js)
文章目录 想要读这个表格,并且求第二列所有价格的和方法一:通过添加文件输入元素上传csv完整(正确)代码之前的错误部分因为价格是小数,所以下面的代码出错。如果把parseFloat改成parseInt,那么求和没有意义…...
Pyraformer复现心得
Pyraformer复现心得 引用 Liu, Shizhan, et al. “Pyraformer: Low-complexity pyramidal attention for long-range time series modeling and forecasting.” International conference on learning representations. 2021. 代码部分 def long_forecast(self, x_enc, x_m…...
成绩排序c++
说明 给出了班里某门课程的成绩单,请你按成绩从高到低对成绩单排序输出,如果有相同分数则名字字典序小的在前。 输入格式 第一行为nn(0<n<200<n<20),表示班里的学生数目; 接下来的nn行,每行为每个学生的名字和他的…...

人脸检测之MTCNN算法网络结构
MTCNN(Multi-task Cascaded Convolutional Networks)是一种用于人脸检测和关键点检测的深度学习模型,特别适合在复杂背景下识别出多尺度的人脸。它通过多任务学习来实现人脸检测和人脸关键点定位(如眼睛、鼻子、嘴巴的位置&#x…...
蓝桥杯顺子日期(填空题)
题目:小明特别喜欢顺子。顺子指的就是连续的三个数字:123、456 等。顺子日期指的就是在日期的 yyyymmdd 表示法中,存在任意连续的三位数是一个顺子的日期。例如 20220123 就是一个顺子日期,因为它出现了一个顺子:123&a…...

Java云HIS医院管理系统源码 病案管理、医保业务、门诊、住院、电子病历编辑
云HIS系统优势 (1)客户/用户角度 无需安装,登录即用 多终端同步,轻松应对工作环境转换 系统使用简单、易上手,信息展示主次分明、重点突出 极致降低用户操作负担:关联功能集中、减少跳转,键盘快…...
【C++的vector、list、stack、queue用法简单介绍】
【知识预告】 vector的介绍及使用list的介绍及使用list与vector的对比stack的介绍和使用queue的介绍和使用priority_queue的介绍和使用 1 vector的介绍及使用 1.1 vector的介绍 vector是表示可变大小数组的序列容器和数组类似,vector也采用连续存储空间来存储元…...

git中使用tag(标签)的方法及重要性
在Git中打标签(tag)通常用于标记发布版本或其他重要提交。 Git中打标签的步骤: 列出当前所有的标签 git tag创建一个指向特定提交的标签 git tag <tagname> <commit-hash>创建一个带注释的标签,通常用于发布版本 git…...

【专题】2024年文旅微短剧专题研究报告汇总PDF洞察(附原数据表)
原文链接: https://tecdat.cn/?p38187 当今时代,各类文化与消费领域呈现出蓬勃发展且不断变革的态势。 微短剧作为新兴内容形式,凭借网络发展与用户需求,从低成本都市题材为主逐步走向多元化,其内容供给类型正历经深…...
celery加速爬虫 使用flower 可视化地查看celery的实时监控情况
重点: celery ==5.4.0 python 3.11 flower ==2.0.1 请对齐celery与flower的版本信息,如果过低会导致报错 报错1: (venv) PS D:\apploadpath\pythonPath\Lib\site-packages> celery -A tasks flower Traceback (most recent call last):File …...

Angular进阶之十:toPromise废弃原因及解决方案
背景 Rxjs从V7开始废弃了toPromise, V8中会删除它。 原因 1:toPromise()只返回一个值 toPromise()将 Observable 序列转换为符合 ES2015 标准的 Promise 。它使用 Observable 序列的最后一个值。 例: import { Observable } from "rxjs"; ………...

python实现RSA算法
目录 一、算法简介二、算法描述2.1 密钥产生2.2 加密过程2.3 解密过程2.4 证明解密正确性 三、相关算法3.1 欧几里得算法3.2 扩展欧几里得算法3.3 模重复平方算法3.4 Miller-Rabin 素性检测算法 四、算法实现五、演示效果 一、算法简介 RSA算法是一种非对称加密算法,…...
可灵开源视频生成数据集 学习笔记
目录 介绍 可灵团队提出了四个模块的改进: video caption 新指标 vtss 动态质量 静态质量 视频自然性 介绍 在视频数据处理中,建立准确且细致的条件是关键,可灵团队认为,解决这一问题需要关注三个主要方面: 文本…...

告别软文营销瓶颈!5招助你突破限制,实现宣传效果最大化
在当今信息爆炸的时代,软文营销作为品牌推广的重要手段之一,面临着日益激烈的竞争和受众日益提高的辨别力。传统的软文营销方式往往难以穿透消费者的心理防线,实现有效的信息传递和品牌塑造。为了突破这一瓶颈,实现宣传效果的最大…...

秋冬进补防肥胖:辨证施补,健康过冬不增脂
中医理论中的秋冬“封藏” 在中医理论中,认为秋冬季节是人体“封藏”的时期,而“封藏”指的是秋冬季节人体应当减少消耗,蓄积能源,此时进补可以使营养物质易于吸收并蓄积于体内,从而增强体质和抵抗力,为来…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

ui框架-文件列表展示
ui框架-文件列表展示 介绍 UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...

C++--string的模拟实现
一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现,其目的是加强对string的底层了解,以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量,…...

云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...