[TS] 分享一个类型支持非常完善的 object path package - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
EasilyJS

[TS] 分享一个类型支持非常完善的 object path package

  •  
  •   EasilyJS 2023 年 5 月 29 日 1535 次点击
    这是一个创建于 1062 天前的主题,其中的信息可能已经有所发展或是发生改变。

    之前用过涉及到 object path 的一些 package ,但是发现这些 packages 的类型支持不是特别完善,比如 lodash, react-hook-form, formik, mongodb client 等。

    这些 packages ,或多或少存在的几个问题是:

    • 没有类型约束,不能提供代码自动完成;
    • 生成的 path 不是标准的,大部分只支持 'a.b.c' 这种,这种 path 有一些缺点,与其他 package 不统一(例如 yup ),不能区分 object/array ,大部分都是判断如果是数字就当成数组处理,但是实际可能是 number key 的 object ;
    • 不能通过 path 反推出对应的类型。

    因为我在写 react-happy-form 这个开源项目的时候,对 object path 这块需求比较高,所以就自己单独写了 object-standard-path 这个 package:

    • 提供了非常完善的类型支持(包括对一些特殊类型 any, Map 等的处理);
    • 生成的是标准的 path ;
    • 支持使用 path 对类型进行反推。

    使用起来也非常简单,没有任何依赖,1k 不到的包体积,如果有需要的话,大家可以试试 :)

    Repo 地址: https://github.com/react-earth/object-standard-path

    可以的话,可以小点个 star ,每个 star 都是我以后持续更新的动力,感谢!

    import { Path, PathValue, pathGet, pathSet } from 'object-standard-path'; type Test = { value: string; array: { value: string; }[]; }; type TestPath = Path<Test>; // result: "value" | "array" | `array[${number}]` | `array[${number}].value` type TestPathValue = PathValue<Test, 'array[0]'>; // result: { value: string } const object = { array: [ { value: 1, }, ], }; const result = pathGet(object, 'array[0].value'); // result: 1 pathSet(object, 'array[0].value', 2); // result: { array: [{ value: 2 }] } 
    10 条回复    2023-05-30 15:43:47 +08:00
    mxT52CRuqR6o5
        1
    mxT52CRuqR6o5  
       2023 年 5 月 29 日
    typescript 的泛型实现有没有办法 benchmark ?我现在就有用到类似的东西,感觉性能不是很理想
    EasilyJS
        2
    EasilyJS  
    OP
       2023 年 5 月 29 日
    没试过 benchmark ,要找一些有没有相关工具,不过我试了一些比较复杂的数据结构,感觉推断速度还是比较快的
    codehz
        3
    codehz  
       2023 年 5 月 29 日
    我觉得对象深度修改还是 lens/optics 的思路好,不依赖 ts 的模版字符串,不需要运行时和编译期 parse 路径两次(然后可能实现不一致)
    用 template literal 的时候,数字要先组合到字符串里,然后再 parse 回来,这在我看来是有点浪费的行为(
    当然用字符串也有字符串的好处,比如说可以整个当作 key 来使用但 lens 的组合也可以提供这种能力
    ----
    当然最好的方法还是从源头去掉这个需求
    什么情况会用到 path ,如果是为了 form 的递归,那可能有别的思路比如 hookstate 那样直接在状态管理库上就把递归的问题处理好了,拿到的直接就是一个 State<T, Extensions>对象,T 是最终的类型,不需要考虑原始的容器,对它进行更新即可
    当然我这只是个人的想法,实际应用可能也有不同的问题,hookstate 自己也有很多坑点
    Leviathann
        4
    Leviathann  
       2023 年 5 月 29 日
    这个和 ts toolbelt 的 AutoPath / type-fest 的 Get 有什么区别
    EasilyJS
        5
    EasilyJS  
    OP
       2023 年 5 月 30 日
    @codehz 目前我 object path 使用最多的场景是 form 表单的构建,针对那些特别复杂的表单需要拆分成若干层级的子组件,每个子组件使用的整个 form 状态的部分 path ,为了保持 form 的健壮性,需要对 path 进行强约束,避免手误导致意料之外的 bug ,如果 form 状态调整了,也可以针对 ts 报错的子组件进行快速调整。
    codehz
        6
    codehz  
       2023 年 5 月 30 日   1
    @EasilyJS 看了一眼 happy-form 的使用场景,确实有其合理性,包括其中推导参数类型的内容
    但是如果像我说的用 hookstate 这样的库的话,就可以直接使用原生的.和[]语法来引用子属性,做类似<input {...bind(state.object.array[1].value)} /> 这样的写法,或者也可以直接把提取出来的子 state 传递给自定义组件,就好像它只是普通的 value 一样,hookstate 的 validator 也能在这个提取出来的子 state 上定义,从而从根源上消除了对 object path 的需求
    ----
    此外我担心 object-path 有其局限性,比如关于特殊字符的处理,js object 里可以用任意符号做 key ,当使用了这样的 key 的时候,就很难有一个“正确的”处理方案,当然可以一开始就直接禁止,typescript 里检测到特殊符号就返回 never
    现在的情况是完全没限制,然后 Path<{"a.b": number}>这样的,就会直接出错
    EasilyJS
        7
    EasilyJS  
    OP
       2023 年 5 月 30 日
    @codehz 确实目前一些特殊字符不是特别好处理,只能是避免使用,比较好奇如果是 <input {...bind(state.object.array[1].value)} 这种方式,确实能比较好的设置 value ,但是 onChange 回调的时候,如何把值设置回去呢?
    EasilyJS
        8
    EasilyJS  
    OP
       2023 年 5 月 30 日
    @Leviathann 我试了下你提到的 type-fest 的 Get ,目前只能通过 path 拿到 value ,并不支持生成 path ,另外没有包含 path get/set 的实现,感觉使用场景比较局限
    codehz
        9
    codehz  
       2023 年 5 月 30 日
    @EasilyJS hookstate 可以直接 set(xxx) (当然 get value 也得用.get(),不然拿到的是 State 对象),实际上是用 proxy 实现的
    EasilyJS
        10
    EasilyJS  
    OP
       2023 年 5 月 30 日
    @codehz 看了下 hookstate 的示例,用 proxy 确实可以实现,但感觉稍微有点 hack (虽然 Mobx 也是类似原理),而且这样的话不太容易和其他第三方库兼容,比如我需要配合 yup 验证库,去标记错误状态,yup 只会给一个 error 的 path 数组
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1350 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 37ms UTC 17:15 PVG 01:15 LAX 10:15 JFK 13:15
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86