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.
V4 is considered unstable and there will probably be some breaking changes as I work out the types and functions.
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
Futurewhich shows the types for possible errors
@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-tsmisn't as complex asfp-ts. It doesn't use things likeMonoidorApplicativeand just exports the data types and functions that developers will use most use cases.fp-tsmuses simpler types internally.fp-tsmisesmonly.fp-tsmhas robust documentation and presents things to developers in a way that they can learn about these concepts if they are not familiar with them.
This projet builds on the success and foundation of many other libraries.
- Effect
- fp-ts
- Boxed
- ReScript
The MIT License (MIT)