首页 > 解决方案 > 从 fp-ts 中的项目数组中删除选项

问题描述

假设我有以下类型:

type ValidatedInput = Readonly<{
  id: string
}>

type GetStructures = (id: string) => TaskEither<ServiceError, ReadonlyArray<SomeStructure>>

type GetOtherStructures = (ids: ReadonlyArray<string>) => TaskEither<ServiceError, ReadonlyArray<SomeOtherStructure>>

type LookupThings = (initialInput: ReadonlyArray<SomeStructure>, additionalInput: ReadonlyArray<SomeOtherStructure>) => TaskEither<ServiceError, ReadonlyArray<Option<ResponseType>>>

export type Deps = Readonly<{
  getStructures: GetStructures
  getOtherStructures: GetOtherStructures
  lookupThings: LookupThings
}>

export type Ctx<T> = ReaderTaskEither<Deps, Error, T>

以及以下错误处理助手:

type Code = 'INVALID_ENTITY' | 'INVALID_API_KEY'
export const fromCode = (code: Code): Error => ({
  tag: 'validation',
  code
})

然后我创建一个像这样的函数:

const constructFullData = (input: ValidatedInput): Ctx<ReadonlyArray<Option<ResponseType>>> => (
  (deps: Deps): TE.TaskEither<Error, ReadonlyArray<Option<ResponseType>>> => (
    pipe(
      deps.getStructures(input.id),
      TE.map((structs) => pipe(
        deps.getOtherStructures([structs[0].higherOrderId]),
        TE.chain(otherStructs => pipe(
          deps.lookupThings(structs, otherStructs),
          TE.chain(results => {
            if (results.filter(isNone).length !== results.length) {
              return TE.left(fromCode('INVALID_ENTITY')) // I'm not sure what a better way to filter this is. Ideally the return type of this  function wouldn't have an Option in it
            } else {
              return TE.right(results)
            }
          })
        ))
      )),
      TE.flatten
    )
  )
)

这一切都编译得很好,但我真正想要的是返回一个没有过滤掉的数组,如果没有则引发适当的错误!

天真的方法是将 Right 路径的返回更改为:

return TE.right(results.map(u => u.value))

但这不会编译,抛出以下错误:

Property 'value' does not exist on type 'None'.
64               return TE.right(usagesWithDrones.map(u => u.value))

如何应用这种过滤?

标签: typescriptfunctional-programmingfp-ts

解决方案


将选项列表折叠成一个呢?就像是:

import * as TE from "fp-ts/TaskEither";
import * as ROA from "fp-ts/ReadonlyArray";
import * as T from "fp-ts/Task";
import * as E from "fp-ts/Either";
import * as O from "fp-ts/Option";
import { pipe } from "fp-ts/pipeable";
import { constant, flow, Lazy } from "fp-ts/lib/function";

const value = TE.right<"ThisErrorDoesntExist", readonly O.Option<number>[]>(
  ROA.of(O.some(42))
);

const arrayOfNumbersMonoid = ROA.getMonoid<number>();

type Result = E.Either<"FoundNoneInList", readonly number[]>;

const rightEmptyListOfNumbers: Result = E.right([]);

const leftFoundNoneInList: Lazy<Result> = constant(E.left("FoundNoneInList"));

const reducer = (acc: typeof rightEmptyListOfNumbers, next: O.Option<number>) =>
  pipe(
    acc,
    E.chain((numbers) =>
      pipe(
        next,
        O.fold(
          leftFoundNoneInList,
          flow(ROA.of, (n) => arrayOfNumbersMonoid.concat(numbers, n), E.right)
        )
      )
    )
  );

const result = pipe(
  value,
  T.map(E.chainW(ROA.reduce(rightEmptyListOfNumbers, reducer)))
);


推荐阅读