ts-unions
TypeScript union types for Maybe and RemoteData with pattern matching.
Installation
npm install @mvst/ts-unions
Usage
Maybe Type
The Maybe
type represents a value that might or might not exist. It's useful for handling
nullable values in a type-safe way.
import { Maybe, M } from "@mvst/ts-unions";
// Creating Maybe values
const someValue: Maybe<number> = M.just(42);
const noValue: Maybe<number> = M.nothing();
// Pattern matching
M.when(someValue, {
just: (value) => `Value is ${value}`,
nothing: () => "No value"
});
M.when(someValue, {
_: () => "This is the fallack case"
});
// Transforming values
const doubled: Maybe<number> = M.map((x) => x * 2, someValue);
// Chaining operations
const chained: Maybe<number> = M.andThen((x) => M.just(x + 1), someValue);
// Unwrapping values
const value: number = M.withDefault(0, someValue); // Unwraps the value or returns 0 if nothing
Maybe API
M.just(value: T): Maybe<T>
- Creates a Maybe with a valueM.nothing(): Maybe<T>
- Creates a Maybe without a valueM.when(pattern: Pattern , maybe: Maybe<T>): R
- Pattern matchingM.map(fn: (value: T) => R, maybe: Maybe<T>): Maybe<R>
- Transform the value if it existsM.andThen(fn: (value: T) => Maybe<R>, maybe: Maybe<T>): Maybe<R>
- Chain Maybe operationsM.isJust(maybe: Maybe<T>): boolean
- Check if Maybe has a valueM.isNothing(maybe: Maybe<T>): boolean
- Check if Maybe has no value
RemoteData Type
The RemoteData
type represents the state of a remote data operation. It's perfect for
handling API calls and async operations.
import { RemoteData, RD } from "@mvst/ts-unions";
// Creating RemoteData values
const data: RemoteData<string> = RD.notAsked();
const loadingData: RemoteData<string> = RD.loading();
const successData: RemoteData<string> = RD.success("Data loaded");
const errorData: RemoteData<string> = RD.error(new Error("Failed to load"));
// Pattern matching
RD.when(data, {
notAsked: () => "Not started",
loading: () => "Loading...",
success: (value) => `Data: ${value}`,
error: (err) => `Error: ${err.message}`
});
RD.when(data, {
success: (value) => `Data: ${value}`,
_: () => "This is the fallback case, all other cases are handled here"
});
// Transforming values
const transformed = RD.map((value) => value.toUpperCase(), successData);
// Chaining operations
const chained = RD.andThen((value) => RD.success(value + "!!!"), successData);
// Unwrapping values
const unwrapped = RD.withDefault("No data", successData); // Unwraps the value or returns "No data" if not success
RemoteData API
RD.notAsked(): RemoteData<T>
- Initial stateRD.loading(): RemoteData<T>
- Loading stateRD.success(value: T): RemoteData<T>
- Success state with valueRD.error(error: Error): RemoteData<T>
- Error state with errorRD.when(pattern: Pattern, data: RemoteData<T>): R
- Pattern matchingRD.map(fn: (value: T) => R, data: RemoteData<T>): RemoteData<R>
- Transform success valueRD.andThen(fn: (value: T) => RemoteData<R>, data: RemoteData<T>): RemoteData<R>
- Chain RemoteData operationsRD.isNotAsked(data: RemoteData<T>): boolean
- Check if not askedRD.isLoading(data: RemoteData<T>): boolean
- Check if loadingRD.isSuccess(data: RemoteData<T>): boolean
- Check if successRD.isError(data: RemoteData<T>): boolean
- Check if error
Features
- The library is tree-shakable
- All the values are immutable and that operations like
map
andandThen
never modify the original value. - All the values are serializable (e.g.: you can easily store them in local storage or send them over the network).
- All the functions in the libray are curried, so you can partially apply them and create reusable functions.
Currying and Composition
This makes it easy to create reusable functions and compose them together.
The library also provides compose
and pipe
utilities to help with function composition, although
you might already have access to similar functions if you're using any functional programming library.
Currying Examples
Maybe
import { Maybe, M } from "@mvst/ts-unions";
// Create reusable functions by partially applying map
const double = M.map((x: number) => x * 2);
const addOne = M.map((x: number) => x + 1);
// Use them with different Maybe values
const value1 = M.just(42);
const value2 = M.just(10);
const doubled1 = double(value1); // Just(84)
const doubled2 = double(value2); // Just(20)
// Compose functions using pipe
const doubleAndAddOne = pipe(double, addOne);
const result = doubleAndAddOne(value1); // Just(85)
RemoteData
import { RemoteData, RD } from "@mvst/ts-unions";
// Create reusable functions by partially applying map
const format = RD.map((value: string) => value.toUpperCase());
const addTimestamp = RD.map((value: string) => `${value} (${new Date().toISOString()})`);
// Use them with different RemoteData values
const data = RD.success("Hello");
const formatted = format(data); // Success("HELLO")
// Compose functions using pipe
const formatAndAddTimestamp = pipe(format, addTimestamp);
const result = formatAndAddTimestamp(data1); // Success("HELLO (2024-03-21T12:00:00.000Z)")
Contributing
Please read CONTRIBUTORS.md for details on our code of conduct and the process for submitting pull requests.
License
This project is licensed under the MIT License - see the LICENSE file for details.