fp-ts Json 模块完全指南:基于 Either 的安全 JSON 解析与序列化

发布时间:2026/9/23 23:45:20

fp-ts Json 模块完全指南:基于 Either 的安全 JSON 解析与序列化 fp-ts Json 模块完全指南基于 Either 的安全 JSON 解析与序列化【免费下载链接】fp-tsFunctional programming in TypeScript项目地址: https://gitcode.com/gh_mirrors/fp/fp-ts导读fp-ts/Json模块自 v2.10.0 起提供为 TypeScript 函数式编程提供了不抛异常的 JSON 解析与序列化方案它把 JavaScript 内置的JSON.parse/JSON.stringify包装成返回Eitherunknown, Json的纯函数让错误成为可组合、可追踪的一等公民。读完本文你将掌握Json、JsonArray、JsonRecord三个核心类型的准确含义学会用parse安全解析字符串、用stringify安全序列化任意值并能通过pipe、Either的各种组合子把它们无缝接入 fp-ts 的任意函数式管线中。一、模块概览为什么 JSON 操作需要函数式封装JavaScript 原生的JSON.parse与JSON.stringify是可能抛异常的函数解析一段非法 JSON 会抛出SyntaxError序列化含循环引用的对象会抛出TypeError。在 fp-ts 的编程范式里异常流无法被pipe、map、chain等组合子感知会破坏类型的纯净性。Json模块给出的答案是用 tryCatch位于src/Either.ts把这两个 API 包装成返回Eitherunknown, Json的纯函数。tryCatch的核心实现非常直白见 src/Either.ts#L1393-L1399export const tryCatch E, A(f: LazyArgA, onThrow: (e: unknown) E): EitherE, A { try { return right(f()) } catch (e) { return left(onThrow(e)) } }即正常返回right(结果)任何异常都经onThrow转换后放入left。Json模块传给onThrow的是identity见 src/Json.ts因此错误类型就是unknown原始异常对象如SyntaxError、TypeError会被原样保留在Either的左侧供后续mapLeft精确检查。从模块导出看src/index.ts在第 58 行import * as json from ./Json、第 385 行将整个模块作为json命名空间导出因此你可以用import * as J from fp-ts/Json或import { json } from fp-ts两种方式引入。二、类型体系Json / JsonArray / JsonRecordJson模块定义了三个互相递归的类型完整刻画了合法 JSON 值的形态见 src/Json.ts#L10-L22export type Json boolean | number | string | null | JsonArray | JsonRecord export interface JsonRecord { readonly [key: string]: Json } export interface JsonArray extends ReadonlyArrayJson {}类型定义说明Jsonboolean \| number \| string \| null \| JsonArray \| JsonRecordJSON 值的联合类型即值层面的完整定义JsonRecordreadonly [key: string]: Json键为字符串、值为Json的只读记录即 JSON 对象JsonArrayextends ReadonlyArrayJsonJson元素的只读数组即 JSON 数组几个值得注意的设计细节没有undefined、函数、Symbol、bigint这些都不是合法 JSON 值因而被类型系统直接排除。dtslint/Json.ts中用ts-expect-error断言了undefined、箭头函数、Symbol()、含undefined字段的对象等都无法通过stringifyJson的类型检查见 dtslint/Json.ts#L9-L20。对象与数组是只读readonly形态JsonRecord的索引签名和JsonArray的数组类型都带readonly与 fp-ts 一贯倡导的不可变数据结构风格一致也方便与ReadonlyArray、ReadonlyRecord等模块对接。递归结构Json引用JsonArray与JsonRecord而后两者又引用Json从而支持任意深度的嵌套 JSON对象套数组、数组套对象……。三、parse安全解析 JSON 字符串parse将一段 JSON 字符串转换为Json类型对应JSON.parse其签名与实现如下export declare const parse: (s: string) Eitherunknown, Json // 实现src/Json.ts#L37 export const parse (s: string): Eitherunknown, Json tryCatch(() JSON.parse(s), identity)行为语义输入合法 JSON→ 返回E.right(json)其中json已被JSON.parse反序列化为实际 JavaScript 值输入非法 JSON→ 底层JSON.parse抛出SyntaxError被tryCatch捕获后包装为E.left(syntaxError)异常对象本身原样保留。官方示例可直接运行import * as J from fp-ts/Json import * as E from fp-ts/Either import { pipe } from fp-ts/function assert.deepStrictEqual(pipe({a:1}, J.parse), E.right({ a: 1 })) assert.deepStrictEqual( pipe({a:}, J.parse), E.left(new SyntaxError(Unexpected token }, {a:} is not valid JSON)) )上面第二个断言展示了一个关键事实解析失败的left分支携带的是真实的SyntaxError实例其message与原生JSON.parse抛出的完全一致便于日志记录或mapLeft分类处理。测试验证在 test/Json.ts#L7-L13 中这两个场景均被测试用例覆盖it(parse, () { U.deepStrictEqual(pipe({a:1}, _.parse), E.right({ a: 1 })) U.deepStrictEqual( pipe({a:}, _.parse), E.left(new SyntaxError(Unexpected token }, {a:} is not valid JSON)) ) })四、stringify安全序列化任意 JavaScript 值stringify将任意 JavaScript 值转换为 JSON 字符串对应JSON.stringify签名与实现如下export declare const stringify: A(a: A) Eitherunknown, string // 实现src/Json.ts#L60-L67 export const stringify A(a: A): Eitherunknown, string tryCatch(() { const s JSON.stringify(a) if (typeof s ! string) { throw new Error(Converting unsupported structure to JSON) } return s }, identity)行为语义与裸用JSON.stringify相比stringify有两个层次的保护异常捕获JSON.stringify遇到循环引用时会抛出TypeError: Converting circular structure to JSON该异常被tryCatch捕获并放入left。返回类型守卫JSON.stringify在遇到undefined、函数或Symbol作为顶层输入时会返回undefined而非字符串。实现里显式检查typeof s ! string一旦出现这种情况就抛出new Error(Converting unsupported structure to JSON)从而保证right分支永远是真正的字符串杜绝看似成功实则拿到 undefined的隐性 bug。官方示例可直接运行import * as E from fp-ts/Either import * as J from fp-ts/Json import { pipe } from fp-ts/function assert.deepStrictEqual(J.stringify({ a: 1 }), E.right({a:1})) const circular: any { ref: null } circular.ref circular assert.deepStrictEqual( pipe( J.stringify(circular), E.mapLeft((e) e instanceof Error e.message.includes(Converting circular structure to JSON)) ), E.left(true) )第二个断言展示了stringify与E.mapLeft的经典配合序列化失败不再是静默的运行时异常而是可以被函数式地检查、映射与分支处理。测试验证test/Json.ts#L15-L35 覆盖了四种典型输入输入期望结果{ a: 1 }E.right({a:1})含循环引用的对象E.left(...)且message含Converting circular structure to JSON类型化对象{ name: Giulio, age: 45 }E.right({name:Giulio,age:45})undefined顶层E.left(new Error(Converting unsupported structure to JSON))其中最后一个用例直接验证了返回类型守卫分支_.stringify(undefined as any)必须返回E.left(new Error(Converting unsupported structure to JSON))。类型层面的约束虽然stringify的类型签名是A(a: A) Eitherunknown, string接受任意类型但配合Json类型使用时可获得更强的编译期保障。dtslint/Json.ts展示了类型测试的意图以下调用必须报类型错误ts-expect-errorstringifyJson(undefined)、stringifyJson(() {})、stringifyJson(Symbol())、stringifyJson({ a: undefined })见 dtslint/Json.ts#L9-L20而合法的 JSON 形状如{ a: a, b: 1 }及其数组、展开副本可以顺利通过见 dtslint/Json.ts#L22-L37。五、组合使用把 parse / stringify 接入函数式管线由于parse与stringify都返回Either它们可以无缝接入 fp-ts 的Either组合子体系。官方示例和 dtslint 用例给出了典型用法1. 链式校验与转换import * as E from fp-ts/Either import { pipe } from fp-ts/function // 解析 → 校验 → 转换全程类型安全 const result pipe( {a:1}, J.parse, E.chain((json) /* 在这里对 json 做进一步校验/转换返回 Eitherunknown, X */ E.right(json)), E.map((x) /* 转换成功后的值 */ x) )2. 用 chainFirst 挂接副作用dtslint 中的用例pipe(E.right(a), E.chainFirst(_.stringify))见 dtslint/Json.ts#L39-L40表明stringify可直接用于chainFirst在保持左侧值不变的同时把序列化结果作为校验步骤——若序列化失败整个管道立即短路为left。// $ExpectType Eitherunknown, string pipe(E.right(a), E.chainFirst(_.stringify))3. 错误分类处理const classify (e: unknown): string e instanceof SyntaxError ? JSON 语法错误: ${e.message} : e instanceof Error ? 其他错误: ${e.message} : 未知错误: ${String(e)} const safeParse (s: string) pipe(s, J.parse, E.mapLeft(classify))六、与其他模块的关系及版本背景依赖Json模块仅依赖EithertryCatch与functionidentity依赖面极小可放心引入见 src/Json.ts#L4-L5。版本引入该模块于 v2.10.0 加入CHANGELOG 中 addJsonmodule 条目。CHANGELOG 同时记录了后续版本中原先分散在其他模块的Json类型、parseJSON、stringifyJSON等旧 API 被标记废弃官方明确建议使用Json模块替代见 CHANGELOG.md。因此新代码应统一从fp-ts/Json导入。七、小结与使用建议fp-ts/Json模块的价值在于把最容易悄悄抛异常的两个 JSON 操作变成了显式、可组合、类型可追踪的Either计算。核心要点回顾Json/JsonArray/JsonRecord三个递归类型精确刻画了合法 JSON 值天然排除undefined、函数等非法输入parse返回Eitherunknown, Json解析失败时左侧携带原始SyntaxErrorstringify返回Eitherunknown, string既捕获循环引用异常又用显式检查杜绝顶层值为 undefined 却返回非字符串的隐患二者均可通过pipe、chain、chainFirst、mapLeft等组合子接入任意 Either 管线错误处理从try/catch 包围升级为数据流中的分支。推荐的使用姿势在解析外部输入HTTP 响应体、配置文件、本地存储读取时一律用J.parseE.mapLeft显式分类错误在序列化可能含循环引用或顶层非字符串值的数据时使用J.stringify并在left分支统一记录日志。这样既保留了原生JSONAPI 的性能与语义又让整个错误路径始终处于类型系统的掌控之中。【免费下载链接】fp-tsFunctional programming in TypeScript项目地址: https://gitcode.com/gh_mirrors/fp/fp-ts创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/23 23:45:20

无源定位椭圆法:被动雷达多站融合定位算法解析

简介:面向无源雷达与被动定位研究场景,这份MATLAB源码实现椭圆法目标定位中的关键步骤——多站观测椭圆交点求解。它根据信号到达时间差/频率差信息构建椭圆模型,通过数值迭代计算目标平面位置,可避免手工解算非线性方程的繁琐并降…

2026/9/23 23:45:20

微信表情包怎么导出成图片素材?做图的人看这里

如果你做图、做贴纸、剪视频,大概遇到过这种卡壳——挑了半天,觉得某个微信表情正好贴合主题,想放进画面里。可你翻遍整个微信,就是拿不出那个「文件」。微信表情导出成图片素材,办法是把它发给「表情保存助手」这个公…

2026/9/23 23:45:20

湘楚有才单招:单招路上,信息差才是最大的不公平

同一个班的学生,成绩差不多,备考时间差不多,最后录取结果却可能差很多。问题出在哪里?很多时候,出在信息差上。有的学生知道目标院校今年扩招了,果断报考,顺利上岸;有的学生不知道,保守填报,浪费了分数。有的学生了解某所学校的职测侧重什么方向,提前针对性准备;有的学生一无所…

2026/9/24 0:45:22

基于SpringBoot的仓储管理系统-附源码

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/9/24 0:45:22

ISO 24748-3指南:软件生命周期过程落地与裁剪实战

简介:ISO/IEC/IEEE 24748-3:2020 是一份系统与软件工程领域生命周期管理国际标准,旨在为组织实施 ISO/IEC/IEEE 12207(软件生命周期过程)提供详细指南。该标准共75页,完整英文电子版,适用于软件工程师、系统…

2026/9/24 0:45:22

Linux与Windows交替输出实现原理对比

1. 这道题到底在考什么:从“交替输出”看操作系统思维的本质差异刚看到这个标题——“Linux课后作业,用Windows下批处理和Linux下的shell脚本完成,两文本交替输出”——我第一反应不是写代码,而是笑了。不是笑题目难,是…

2026/9/24 0:45:22

C++与OpenCV实现光学相位测量技术:相移法与三频外差法

1. 光学相位测量技术概述在工业检测、三维形貌测量等领域,光学相位测量技术因其非接触、高精度的特性而广受青睐。其中,相移法结合格雷码和三频外差法是两种主流的绝对相位获取方案。本文将深入解析基于C和OpenCV实现的这两种算法的核心原理与工程实践。…

2026/9/24 0:45:22

Java开发环境搭建与Tomcat配置实战指南

1. Java开发环境搭建全攻略 作为一名Java开发者,我深知环境配置是每个新手面临的第一个挑战。记得我刚入门时,光是配置JDK和Tomcat就折腾了大半天。今天我就把多年积累的环境配置经验整理成这份详细指南,帮你避开那些我踩过的坑。 1.1 JDK安…

2026/9/24 0:40:22

Java服务端发丝级抠图:ONNX Runtime部署matting模型实战

简介:该资源是一套基于ONNX模型的发丝级人像抠图与背景替换Java实现源码,面向希望将深度学习模型集成到Java应用中的开发者,以及研究图像分割与高精度抠图的技术人员。项目以Java为核心语言,借助ONNX实现跨框架模型加载与推理&…

2026/9/23 12:07:00

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/24 0:00:21

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:21

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:21

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码