Skip to content

Repository files navigation

Introduction

fp-tsm adds the Option, Result and Future data types along with a suite of utility functions to work these and other data structures in a safe and point-free style.

This is continuation of fp-ts and a super lightweight alternative to effect.

You should use this library if you want just some very basic FP concepts in your app, you need to migrate from fp-ts to use an ESM only version while maintaining type compatibiltiy, or if you feel like effect is too much for what you need.

Note

V4 is considered unstable and there will probably be some breaking changes as I work out the types and functions.

Why?

Traditional JavaScript code often requires many null checks, try/catch blocks, and Promise error handling which can become difficult to read and maintain. For example:

const userSchema = z.object({
  email: z.string().email(),
  settings: z.object({
    theme: z.string().optional(),
  }).optional(),
})

async function getUserData(id: string): Promise<string> {
  try {
    const response = await fetch(`/api/users/${id}`)
      .catch((error) => {
        throw new Error(`Network error: ${error}`)
      })

    if (!response) {
      throw new Error("No response received")
    }

    const user = userSchema.parse(await response.json())

    const settings = user.settings

    if (!settings || !settings.theme) {
      return "default-theme"
    }

    return settings.theme
  } catch (error) {
    console.error("Failed to get user data:", error)
    return "default-theme"
  }
}

This code is error-prone and requires careful attention to null checks and error handling. fp-tsm provides tools to handle these cases more elegantly. Also notice how the return type is just a Promise<string>. We really don't know if this can fail, and what types of errors could happen.

Here's how the same code could be written using fp-tsm:

const userSchema = z.object({
  email: z.string().email(),
  settings: z.object({
    theme: z.string().optional(),
  }).optional(),
})

const getUserData = (
  id: string,
): Future.Future<Error | ZodError, string> =>
  pipe(
    Future.fetch(`/api/users/${id}`),
    Future.mapLeft((error) => Error(`Network error: ${error}`)),
    Future.flatMap((res) => Future.fromPromise(res.json())),
    Future.mapLeft((error) => Error(`Failed to get user data: ${error}`)),
    Future.flatMap((data) => {
      const parsed = userSchema.safeParse(data)
      return parsed.success ? Future.right(parsed.data) : Future.left(parsed.error)
    }),
    Future.map((user) =>
      pipe(
        Option.of(user?.settings?.theme),
        Option.getOrElse(() => "default-theme"),
      )
    ),
  )

This version is:

  • More declarative and easier to follow
  • Handles errors gracefully without try/catch blocks
  • Manages null checks through Option
  • Processes async operations with Future which shows the types for possible errors

fp-ts

@jvlk/fp-tsm is a fork of fp-ts that supports ESM and removes more of the complex functional concepts. It's intended to take the best features from languages like Rust and ReScript and make them easy to use for TypeScript developers. fp-tsm supports interop with fp-ts and you should be able to migrate most functions over.

Here is how fp-tsm diverges from fp-ts:

  • fp-tsm isn't as complex as fp-ts. It doesn't use things like Monoid or Applicative and just exports the data types and functions that developers will use most use cases.
  • fp-tsm uses simpler types internally.
  • fp-tsm is esm only.
  • fp-tsm has robust documentation and presents things to developers in a way that they can learn about these concepts if they are not familiar with them.

Special Thanks

This projet builds on the success and foundation of many other libraries.

  • Effect
  • fp-ts
  • Boxed
  • ReScript

License

The MIT License (MIT)

Releases

Contributors

Languages