Unlock the full potential of working with TaskEither by utilizing its powerful functionality in wrapping an Option with

After exploring various examples of using TaskEither for tasks like making HTTP requests or reading files, I am now attempting to simulate the process of retrieving an item from a database by its ID. The possible outcomes of this operation could be:

  1. The item is found
  2. No item was found with the specified ID
  3. An error occurred (e.g., DB connection issue)

An appropriate interface for handling such scenarios would be

TaskEither<Error, Option<A>>
:

type TaskEO<A> = TaskEither<Error, Option<A>>

Since the result will be sent as an HTTP response (in response to a GET query), it is crucial to clearly differentiate between the three potential outcomes mentioned above. The corresponding response codes would be:

  1. 200 + payload
  2. 404
  3. 500

I have devised the following code snippet to map these scenarios into their respective HTTP responses:

import * as O from "fp-ts/Option";
import * as TE from "fp-ts/TaskEither";
import * as E from "fp-ts/Either";
import { pipe } from "fp-ts/function";

type TaskEO<A> = TE.TaskEither<Error, O.Option<A>>;

const getGoodStuff = (id: string): TaskEO<string> => TE.of(O.some(`result for ${id}`));

const getBadStuff = (id: string): TaskEO<string> =>
  TE.left(new Error(`failed fetching ${id}`));

const getEmptyStuff = (id: string): TaskEO<string> => TE.of(O.none);

getGoodStuff("123")()
  .then((e) =>
    pipe(
      e,
      E.fold(
        (error) => `500: Internal Server Error`,
        (stuff) =>
          pipe(
            stuff,
            O.match(
              () => `404: Not Found Error`,
              (value) => `200: Yay we got: "${value}"`
            )
          )
      )
    )
  )
  .then(console.log);

Feel free to replace the getGoodStuff function call with any other get...Stuff functions and observe how they handle different responses appropriately!

Now comes the question for YOU, dear reader - Do you think there's a more efficient way to structure this composition? Share your thoughts and suggestions on optimizing the code!


EDIT I've refined the code to something like this:

enum HttpResponseCode {
  OK = 200,
  NOT_FOUND = 404,
  INTERNAL_SERVER_ERROR = 500
}

type HttpResponse = {
  code: HttpResponseCode;
  payload: unknown;
}

const toHttpResponse = <A>(e: E.Either<Error, O.Option<A>>): HttpResponse =>
  E.fold(
    (error) => ({ code: HttpResponseCode.INTERNAL_SERVER_ERROR, payload: "Internal Server Error" }),
    O.match(
      () => ({ code: HttpResponseCode.NOT_FOUND, payload: "Resource not found" }),
      (value) => ({ code: HttpResponseCode.OK, payload: value })
    )
  )(e)

This can then be utilized in an Express route handler like so:

async (req, res) => {
      await findStuffById(req.params.stuffId)()
        .then(toHttpResponse)
        .then(({ code, payload }) => res.status(code).send(payload))
    }

Answer №1

Your recent edit has resulted in a clean solution for handling all possible cases. Utilizing functions like fold or match is key to achieving this.

If you frequently encounter the need to match a specific shape and find yourself writing similar function bodies repeatedly, creating a helper function like the following can be beneficial:

function matchTaskEO<A, R>({
  onError,
  onNone,
  onSome,
}: {
  onError: (e: Error) => R,
  onNone: () => R,
  onSome: (a: A) => R,
}) {
  return (taskEO: TaskEO<A>) => E.match(
    onError,
    O.match(onNone, onSome),
  );
}

This helper function can then be used to implement toHttpResponse:

const toHttpResponse = <A>(taskEO: TaskEO<A>) => matchTaskEO<A, HttpResponse>({
  onError: (e) => ({ 
    code: HttpResponseCode.INTERNAL_SERVER_ERROR,
    payload: "Internal Server Error",
  }),
  onNone: () => ({
    code: HttpResponseCode.NOT_FOUND,
    payload: "Resource not found",
  }),
  onSome: (value) => ({ code: HttpResponseCode.OK, payload: value })
})(taskEO);

This approach simplifies the definition process, even though the explicit matching of Either and Option may still seem clear in this scenario.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilize TypeScript to match patterns against either a string or Blob (type union = string | Blob)

I have created a union type called DataType, type TextData = string type BinaryData = Blob type DataType = TextData | BinaryData Now, I want to implement it in a function function processData(data: DataType): void { if (data instanceof TextData) ...

Using Typescript to create a mapped type that allows for making all properties read-only, with the exception of

I encountered a problem where I didn't want to repeatedly rewrite multiple interfaces. My requirement is to have one interface with full writing capabilities, while also having a duplicate of that interface where all fields are set as read-only excep ...

How can we design a return type for a function in Typescript that enforces the exact keys present in the input array K[] to be included in the Record?

I have a function that takes an array of Animals, and returns a map where the keys are the animals and the values are their fur colors: export enum Animals { CAT = 'CAT', DOG = 'DOG', SEAL_PUP = 'SEAL_PUP', } const furC ...

What is the method for obtaining the properties of a type as an array in Typescript?

In the given scenario: class Foo { constructor( private one: string, private two: string, private three: string) { } } Is there a way to create an array containing the type's properties? For example, I need to gene ...

Obtain the appropriate selection in the dropdown based on the model in Angular

I am working on a dropdown menu that contains numbers ranging from 1 to 10. Below is the HTML code for it: <div class="form-group"> <label>{{l("RoomNumber")}}</label> <p-dropdown [disab ...

Invoke the built-in matcher within a Playwright custom matcher

I am in the process of implementing a custom matcher for Playwright based on the information provided in the official documentation on extending expect. In a similar vein to this unanswered discussion, my goal is to invoke an existing matcher after modifyi ...

Combining TypeScript into HTML resulted in an error: Uncaught ReferenceError clickbutton is not defined

Attempting to create a basic CRUD frontend without the use of any frameworks. I am encountering an issue when trying to include a TypeScript file (index.ts) in my index.html, as the functions called within it are showing as undefined. I understand that bro ...

"Utilizing FormData in an IONIC 5 project with

While creating a user profile, I am encountering an issue where the FormData being generated for sending is empty despite all other fields having values. Below is the code snippet from cadastro.ts: import { Component, OnInit } from '@angular/core&ap ...

The type 'Data' is lacking the following attributes from its definition

Being a newcomer to Angular 8, I can't figure out why this error is popping up. If you have any suggestions on how to improve the code below, please feel free to share your tips. The error message reads: Type 'Data' is missing the follo ...

Using ThreeJS to Apply Dual Materials to a Mesh Entity

With ThreeJS, it's possible to incorporate more than one material into an Object3D/Mesh as stated in the documentation. You can either utilize a single Material or an array of Material: Class declaration and constructor for Mesh TypeScript file (exce ...

Consecutive requests to APIs using RxJs

Is it possible to efficiently make sequential API calls using RxJs? The challenge lies in the fact that the first Observable emits an array, and for each item in this array, a custom URL should be set for the next call. Additionally, certain conditions nee ...

Unnecessary Attributes in Type that Should be Automatically Inherited by Child Component

Within my child component, I am creating the Props interface and incorporating it into the React.Component. These Props must then be passed from the parent component to the child component. So far, everything is clear and logical. However, when I extend ...

Adding Images Using Angular 8

I'm encountering difficulties with image upload in the file located at '../src/app/assets/'. Below is the Form I am using: <form [formGroup]="formRegister" novalidate=""> <div class="form-group"> <label for="ex ...

Ways to utilize Subjects for sharing global information in Angular 6

I have been struggling to find an effective way to share data between two components that have the same parent in an Angular application. Currently, I am working with an Angular Material stepper where Step 1 contains one component and Step 2 contains anot ...

What is the process of 'initializing' an object in TypeScript?

Is it possible that retrieving a json from a mongodb database and casting it does not trigger the typescript constructor? What could be causing this issue? I have a Team class export class Team { transformations: { [transformationId: string]: Transfor ...

Adjusting the Material UI Select handleChange function

const handleObjectChange = (event: React.ChangeEvent<{ value: unknown }>) => { const { options } = event.target as HTMLSelectElement; const selectedValues: object[] = []; for (let i = 0, l = options.length; i < l; i += 1) { if ...

Tips for incorporating a mail button to share html content within an Angular framework

We are in the process of developing a unique Angular application and have integrated the share-buttons component for users to easily share their referral codes. However, we have encountered an issue with the email button not being able to send HTML content ...

Enhancing supertest functionality with Typescript

Currently, I am working on extending the functionality of supertest. After referencing a solution from Extending SuperTest, I was able to implement the following example using javascript: const request = require('supertest'); const Test = reque ...

How to display two elements side by side within a div using React

I have an array that looks like this: const arr = [1,2,3,4,5,6,7,8,9,10] I am looking to display the elements in pairs per line within two-dimensional divs. Here is what I have in mind: This represents the main React element: render() { return <di ...

React - retrieving the previous page's path after clicking the browser's "back" button

Imagine I'm on Page X(/path-x) and then navigate to page Y(/path-y). Later, when I click the "back" button in the browser. So my question is, how do I retrieve the value of /path-y in PageX.tsx? Note: I am utilizing react-router-dom ...