繁体   English   中英

使用 `fp-ts` 将 `Task` 从 `Some` 转换或过滤为 `Left`

[英]convert or filter a `Task` from `Some` to `Left` with `fp-ts`

我正在尝试学习如何在io-ts | 中使用类型保护和预测函数 fp-ts ,以及我需要执行以下操作:

我有这个 function:

    createOne: ({ password, ...creatableUser }: CreatableUser): taskEither.TaskEither<ExceptionError, User> => {
      ....
    }

它需要:

  1. 检查用户email是否不存在
  2. 使用给定数据创建用户
  3. 向新用户发送 email 所以我需要这样的东西:

// * Doubt Function 
export const createOne = ({ password, ...creatableUserData }: CreatableUser): taskEither.TaskEither<Error, User> => {
  // Generate the new user
  const newUser: User = {
    ...creatableUserData,
    id: randomUUID(),
  };

  return pipe(
    // * Check that the user doesn't exists yet
    creatableUserData.email,
    findByEmail,
    taskEither.fromTaskOption(() => new Error('User already exists')), // TODO: need to return a error on `Some`
    // * Save the new `User` on the repository
    taskEither.chain(() => save(newUser, password)),
    // * Send the confirmation email to the user
    taskEither.chain(() =>
      sendMail({
        body: 'Welcome to App Team',
      }),
    ),
    taskEither.map(() => newUser),
  );
};

我需要“解析”从taskOption.SometaskEither.Left的响应,但我找不到这样做的方法

这里的类型以获得更好的上下文:

import { randomUUID } from 'crypto';
import { TaskOption } from 'fp-ts/lib/TaskOption';
import { taskEither } from 'fp-ts';
import { TaskEither } from 'fp-ts/lib/TaskEither';
import { pipe } from 'fp-ts/lib/function';

// * Types for context
type CreatableUser = {
  password: string;
  email: string;
  //...
}
type User = {
  email: string;
  id: string;
  //...
}

declare const findByEmail: (email: string) => TaskOption<User>;
declare const save: (user: User, password: string) => TaskEither<Error, void>;
declare const sendMail: (message: {body: string}) => TaskEither<Error, void>;

如果用户已经存在,有人知道如何过滤或使用预测的 function 提前返回?

更新

我能够使用以下代码解决此问题:

export const makeCreateOne =
  (usersRepository: UsersRepository, mailProvider: MailProvider) =>
  ({ password, ...creatableUserData }: CreatableUser): TaskEither<ExceptionError, User> => {
    // Generate the new user
    const newUser: User = {
      ...creatableUserData,
      id: randomUUID(),
    };


    return pipe(
      // * Get the user with the given email
      creatableUserData.email,
      usersRepository.findByEmail,
      // * Return `null` if `Some`
      TO.match(
        // TODO: improve that
        // None: The user doesn't exists, we can create a new user
        () => true,
        // Some: The user already exists, so we don't wanna keep the creation process
        () => null,
      ),
      TE.fromNullable(createExceptionError('User already exists', REQUEST_STATUS.not_found)),
      // * Save the new `User` on the repository
      TE.chain(() => usersRepository.save(newUser, password)),
      // * Send the confirmation email to the user
      TE.chain(() =>
        mailProvider.sendMail({
          body: 'Welcome to App Team',
        }),
      ),
      TE.map(() => newUser),
    );
  };

但是,看起来仍然不太好,如果有人知道如何改进我很感激

如果有人有同样的疑问,这里是我的解决方案:我发现此时创建一个方法来验证 email 是否可用是有意义的,所以我创建了这个接口:

export type UsersRepository = {
  readonly findByEmail: (email: string) => TaskOption<User>;
  readonly findByID: (id: string) => TaskOption<User>;

  readonly save: (user: User, password: string) => TaskEither<ExceptionError, void>;
  readonly update: (user: User, password?: string) => TaskEither<ExceptionError, void>;
  readonly delete: (userID: ID) => TaskEither<ExceptionError, void>;

  readonly all: () => TaskEither<ExceptionError, ReadonlyArray<User>>;

  readonly isUserPasswordValid: (email: string, password: string) => Task<boolean>;
  readonly isEmailAvailable: (email: string) => Task<Boolean>;
};

并将 function 重构为:

export const makeCreateOne =
  (usersRepository: UsersRepository, mailProvider: MailProvider) =>
  ({ password, ...creatableUserData }: CreatableUser): TaskEither<ExceptionError, User> => {
    // Generate the new user
    const newUser: User = {
      ...creatableUserData,
      id: randomUUID(),
    };

    return pipe(
      // * Get the user with the given email
      creatableUserData.email,
      usersRepository.isEmailAvailable,
      TE.fromTask,
      TE.filterOrElse(
        isEmailAvailable => isEmailAvailable,
        () => createExceptionError('Check your password and try again', REQUEST_STATUS.bad),
      ),
      // * Save the new `User` on the repository
      TE.chain(() => usersRepository.save(newUser, password)),
      // * Send the confirmation email to the user
      TE.chain(() =>
        mailProvider.sendMail({
          body: 'Welcome to App Team',
        }),
      ),
      TE.map(() => newUser),
    );
  };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM