fp-ts中如何查找Json类型指定key对应的value值
我可以通过如下代码从JSON字符串中提取值,得到Option类型的返回结果:
可正常运行的代码
import { identity, pipe } from "fp-ts/lib/function"; import * as E from "fp-ts/Either"; import * as O from "fp-ts/lib/Option"; import * as R from "fp-ts/lib/Record"; const jsonString = '{"a": 1}'; const a = pipe( jsonString, (x) => E.tryCatch( () => JSON.parse(x), (reason) => new Error(String(reason)) ), O.fromEither, O.chain(R.lookup("a")) ); console.log(a);
上述代码中E.tryCatch调用返回的类型为Either<Error, any>,R.lookup方法可以接收any类型的入参,因此可以正常运行。
无法正常运行的代码
我不希望直接调用原生JSON.parse方法,而是想要使用fp-ts中Json模块提供的parse方法解析JSON,对应代码如下:
import { identity, pipe } from "fp-ts/lib/function"; import * as J from "fp-ts/lib/Json"; import * as O from "fp-ts/lib/Option"; import * as R from "fp-ts/lib/Record"; const jsonString = '{"a": 1}'; const a = pipe( jsonString, J.parse, O.fromEither, O.chain(R.lookup("a")) // 此处报类型错误 );
尝试在Json类型的返回值上调用R.lookup方法时,会抛出如下类型错误:
Argument of type '<A>(r: Record<string, A>) => Option<A>' is not assignable to parameter of type '(a: Json) => Option<Json>'
报错原因是Json类型是多重类型的联合(boolean | number | string | null | JsonArray | JsonRecord),其中只有JsonRecord类型与Record类型兼容。
问题
如何将Json类型转换为Record类型(或其他支持按键查找对应值的类型)?或是如何将Json类型的对象窄化为JsonRecord类型以实现按键查找?
已尝试的方案
我参考Record模块fromFoldableMap方法的zipObject示例,尝试编写了如下代码,但未能成功运行:
import { identity, pipe } from "fp-ts/lib/function"; import * as J from "fp-ts/lib/Json"; import * as O from "fp-ts/lib/Option"; import * as R from "fp-ts/lib/Record"; const jsonString = '{"a": 1}'; const a = pipe( jsonString, J.parse, O.fromEither, O.map((kvs) => R.fromFoldableMap(last<J.Json>(), R.Foldable)(kvs, identity)), // 此处不生效 O.chain(R.lookup("a")) );
解决方案
直接使用fp-ts Json模块自带的类型守卫J.isRecord做类型窄化即可,不需要额外做类型转换:
import { pipe } from "fp-ts/lib/function"; import * as J from "fp-ts/lib/Json"; import * as O from "fp-ts/lib/Option"; import * as R from "fp-ts/lib/Record"; const jsonString = '{"a": 1}'; const a = pipe( jsonString, J.parse, O.fromEither, // 校验JSON值是否为对象类型,窄化Json联合类型为JsonRecord O.chain((val) => J.isRecord(val) ? O.some(val) : O.none), O.chain(R.lookup("a")) ); console.log(a); // 输出 some(1)
说明
J.isRecord是fp-ts官方提供的类型谓词函数,调用后TypeScript会自动将值的类型从Json联合类型窄化为JsonRecord(即Record<string, Json>的类型别名),完全匹配R.lookup的入参类型要求。- 如果解析出的JSON值不是对象类型(比如是数字、字符串、数组、null),这一步会直接返回
O.none,符合Option类型的错误处理逻辑,不会产生运行时异常。 - 之前尝试的
fromFoldableMap方案不可行,原因是该方法要求入参实现Foldable类型实例,而Json是多个类型的联合,不存在统一的Foldable实例,因此会报类型错误。
如果需要对取到的字段值做进一步类型校验(比如确认字段值为number类型),可以继续搭配自定义类型守卫链式处理:
const aAsNumber = pipe( a, O.chain((val): O.Option<number> => typeof val === 'number' ? O.some(val) : O.none) );
内容的提问来源于stack exchange,提问作者xav
相关产品推荐
相关产品推荐

