You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 03:21:45