見出し画像

カスタムエラーデータをSemigroupでサマリあげるワークアラウンドの紹介

こんにちわ。nap5です。


カスタムエラーデータをSemigroupでサマリあげるワークアラウンドの紹介をしたいと思います。


collectError関数はユーザー定義でnarrowingしていますが、もう少しいい書き方ありそうです。ちょっとぱっと思いつきませんでした。

nut数が偶数の時にエラー出すようにしています。


import { z } from "zod";
import { getSemigroup } from "fp-ts/lib/Array";
import { Either, isLeft as isError } from "fp-ts/lib/Either";
import { TaskEither, tryCatch } from "fp-ts/lib/TaskEither";
import { sequenceT } from "fp-ts/lib/Apply";
import { ApplyPar } from "fp-ts/lib/Task";

type UserId = number;

class CustomError extends Error {
  constructor(message: string, option?: { cause: unknown }) {
    super(message, option);
  }
}

const CustomErrorDataSchema = z.custom<CustomError>();
type CustomErrorData = z.infer<typeof CustomErrorDataSchema>;

const doN = (n: number): TaskEither<CustomErrorData, UserId> => {
  return tryCatch(
    async () => {
      if (n % 2 === 0) {
        return Promise.reject(
          new CustomError(`Something went wrong... [${n}]`)
        );
      }
      return n;
    },
    (e) => e as CustomErrorData
  );
};

const isAllError = (results: Either<CustomErrorData, UserId>[]) => {
  return results.every((result) => isError(result));
};
const hasSomeError = (results: Either<CustomErrorData, UserId>[]) => {
  return results.some((result) => isError(result));
};

const S = getSemigroup<CustomErrorData>();

const collectError = (results: Either<CustomErrorData, UserId>[]) => {
  const errors = results
    .map((result) => {
      if (isError(result)) {
        return result.left;
      }
    })
    .filter((item): item is CustomErrorData => !!item);
  return S.concat(errors, []);
};

(async () => {
  const results = await sequenceT(ApplyPar)(
    doN(1),
    doN(2),
    doN(3),
    doN(4),
    doN(5)
  )();
  console.log(`isAllError`, isAllError(results));
  console.log(`hasSomeError`, hasSomeError(results));
  console.log(collectError(results));
})();


デモコードです。


簡単ですが、以上です。

この記事が気に入ったらサポートをしてみませんか?