【问题标题】:Fetching and mapping data with fp-ts and io-ts使用 fp-ts 和 io-ts 获取和映射数据
【发布时间】:2021-05-10 13:44:32
【问题描述】:

我正在努力将获取的数据“按摩”成我想要的形状,使用fp-ts 进行功能转换,使用io-ts 进行数据验证。

我在寻找什么

我希望 getSchools() 返回描述问题所在的 Error 或经过验证的 Schools 数组。我的代码有些工作,但问题是,如果获取的学校数组中的一所学校未能通过验证,那么一切都会失败。我想只过滤掉那些失败的,然后返回其余的。

我目前的代码

/**
 * API route for all Schools
 */
export default async (_: NextApiRequest, res: NextApiResponse<unknown>) => {
  return new Promise(
    pipe(
      getSchools(),
      fold(
        (e) => of(res.status(400).end(e.message)),
        (v) => of(res.status(200).json(v))
      )
    )
  );
};

/**
 * Handler for fetching Schools
 */
export function getSchools(): TaskEither<Error, Array<School>> {
  return pipe(
    fetch(schoolQuery(schoolQueryBody)),
    chain(mapToschools),
    chain(decode(t.array(School)))
  );
}

function mapToschools(
  inputs: Array<any>
): TaskEither<Error, Array<School>> {
  try {
    return right(inputs.map(mapToschool));
  } catch (e) {
    return left(new Error("Could not map input to school"));
  }
}

export function mapToschool(input: any): School // Can throw Error

export const schoolQueryBody = `...`;

function fetch(query: string): TaskEither<Error, unknown>

export function decodeError(e: t.Errors): Error {
  const missingKeys = e.map((e) => e.context.map(({ key }) => key).join("."));
  return new Error(`Missing keys: ${missingKeys}`);
}

export const decode = <I, A>(Type: t.Decoder<I, A>) => (
  res: I
): TaskEither<Error, A> => {
  return pipe(fromEither(Type.decode(res)), mapLeft(decodeError));
};

【问题讨论】:

  • 通常这是使用称为Validation 的特殊类型完成的。 AFAIK,FP-TS 不包含它,但似乎有一个替代的Applicative 实例用于Either,它允许收集所有错误语义。 More.
  • 验证是使用io-ts 完成的,并且有效,我的问题更多是关于映射和结果数组,可以是左或右,也可以是正好的数组,或类似的东西。
  • 先解码任意数组然后再解码数组项(School)怎么样?
  • 也许可以这样做,但应该可以以某种方式映射结果?然后过滤结果还是什么?

标签: javascript typescript functional-programming fp-ts


【解决方案1】:

我认为您在这里有一些选项可以返回您想要返回的内容,并且我认为 fp-ts / io-ts 的默认行为与您想要的内容并不完全一致。

发生了什么

当您解析t.array 时,只要其中一个值未能解码,您就会失败。在我看来,您想单独尝试解码每个值,而不是使用 t.array

建议

我想我会改为这样说:

import { pipe } from 'fp-ts/lib/function';
import * as ArrayFP from 'fp-ts/lib/Array';

const undecodedSchools: unknown[] = [/* ... school response */];
const schools: School[] = pipe(
  undecodedSchools,
  ArrayFP.map(schoolCodec.decode), // Array<Either<t.Errors, School>>
  ArrayFP.rights, // Takes an Array<Either<E, A>> -> Array<A>
);

这完全摆脱了Either,这可能不是您想要的。如果您想查看错误,可能值得改为:

const schools: {
  left: Error[],
  right: School[],
} = pipe(
  undecodedSchools,
  ArrayFP.map(schoolCodec.decode), // Array<Either<t.Errors, School>>
  ArrayFP.separate, // Takes an Array<Either<A, B>> -> Separated<A[], B[]>
);

这将两种类型的东西从数组中分离出来。

【讨论】:

  • 非常感谢您的回答!我还没有时间尝试它,但很快就会测试它,并得到结果。 :)
  • 花了一些时间,但我终于有时间测试这个实现了,它按我的意愿工作。谢谢! ??
猜你喜欢
  • 1970-01-01
  • 2022-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
  • 2020-05-02
  • 2021-08-01
  • 1970-01-01
相关资源
最近更新 更多