Loading repository data…
Loading repository data…
kennedygitonga / repository
The Option type is a powerful tool for handling values that may or may not be present, inspired by functional programming languages like Rust and Haskell. It provides a type-safe way to avoid common errors associated with null or undefined by explicitly representing the absence of a value.
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
The Option type is a powerful tool for handling values that may or may not be present, inspired by functional programming languages like Rust and Haskell. It provides a type-safe way to avoid common errors associated with null or undefined by explicitly representing the absence of a value.
The Option<T> type represents a value that can either be Some<T> (containing a value of type T) or None (representing the absence of a value). This approach helps prevent runtime errors by forcing developers to handle both cases explicitly.
Some and None cases.null or undefined.To integrate the Option type into your existing TypeScript project:
Option Module: Copy the Option type and its utility functions into a shared module or utility file.fromNullable function to convert existing values that might be null or undefined into Option<T>.null or undefined checks with isSome and isNone functions.import { Option, some, none, isSome, fromNullable } from './option';
function getSomeValue(): string | null {
return Math.random() > 0.5 ? 'Hello' : null;
}
const maybeValue: Option<string> = fromNullable(getSomeValue());
if (isSome(maybeValue)) {
console.log(maybeValue.value); // Safely access the value
} else {
console.log('No value present');
}
Improved code safety and readability, reducing the need for defensive null checks.
Some and None cases.map, flatMap, and unwrapOr to work with Option values.unwrap unless you are certain the value is Some, or handle the potential error.some<T>(value: T): Creates an Option containing a value.none(): Creates an Option representing no value.isSome<T>(option: Option<T>): Returns true if the option is Some.isNone<T>(option: Option<T>): Returns true if the option is None.map<T, U>(option: Option<T>, fn: (value: T) => U): Applies a function to the value if Some, otherwise returns None.flatMap<T, U>(option: Option<T>, fn: (value: T) => Option<U>): Chains operations that return Option.unwrapOr<T>(option: Option<T>, defaultValue: T): Returns the value if Some, otherwise returns the default.unwrap<T>(option: Option<T>, message?: string): Returns the value if Some, otherwise throws an OptionError.filter<T>(option: Option<T>, predicate: (value: T) => boolean): Returns Some if the value passes the predicate, otherwise None.orElse<T>(option: Option<T>, alternative: () => Option<T>): Returns the option if Some, otherwise computes and returns the alternative.import {
Option,
some,
none,
isSome,
isNone,
map,
flatMap,
filter,
unwrap,
unwrapOr,
fromNullable,
and,
or,
liftA2,
matchOption,
mapWithDefault,
} from 'ts-option';
// In-memory user database
let userID = 0;
const userDB: User[] = [];
type User = {
id: number;
name: string;
email: string;
};
// Create a user with validation
function createUser(name: string, email: string): Option<User> {
if (name.trim().length < 3 || !email.includes('@')) {
return none();
}
const user = { id: ++userID, name, email };
userDB.push(user);
return some(user);
}
// Get user by email
function getUserByEmail(userEmail: string): Option<User> {
return fromNullable(userDB.find((user) => user.email === userEmail));
}
// Get user by ID
function getUserById(userId: number): Option<User> {
return fromNullable(userDB.find((user) => user.id === userId));
}
// Update user
function updateUser(user: User): Option<User> {
const index = userDB.findIndex((u) => u.id === user.id);
if (index === -1) {
return none();
}
userDB[index] = user;
return some(user);
}
// Delete user by ID
function deleteUser(userId: number): Option<User> {
const index = userDB.findIndex((u) => u.id === userId);
if (index === -1) {
return none();
}
const user = userDB.splice(index, 1)[0];
return some(user);
}
// Example usage with all Option functionalities
(() => {
// Create users
const user1 = createUser('Alice', 'alice@example.com');
const user2 = createUser('Bob', 'bob@example.com');
const invalidUser = createUser('C', 'charlie');
// Check if users exist
console.log('User1 exists:', isSome(user1)); // true
console.log('Invalid user exists:', isNone(invalidUser)); // true
// Transform user data with map
const userName = map(user1, (u) => u.name);
console.log('User1 name:', unwrapOr(userName, 'Unknown')); // 'Alice'
// Chain operations with flatMap
const updatedUser = flatMap(user1, (u) =>
updateUser({ ...u, name: 'Alicia' })
);
console.log('Updated user:', unwrapOr(updatedUser, null)); // { id: 1, name: 'Alicia', email: 'alice@example.com' }
// Filter users
const longNameUser = filter(user2, (u) => u.name.length > 3);
console.log('Long name user:', unwrapOr(longNameUser, null)); // { id: 2, name: 'Bob', email: 'bob@example.com' }
// Handle non-existent user with unwrap and error
const nonExistentUser = getUserByEmail('nonexistent@mail.com');
try {
unwrap(nonExistentUser);
} catch (e) {
console.log('Error:', (e as Error).message); // 'Attempted to unwrap a None value'
}
// Provide default with unwrapOr
const defaultUser = unwrapOr(nonExistentUser, {
id: ++userID,
name: 'Guest',
email: 'guest@example.com',
});
console.log('Default user:', defaultUser); // { id: 3, name: 'Guest', email: 'guest@example.com' }
// Combine two options with and
const userPair = and(user1, user2);
console.log('User pair:', unwrapOr(userPair, null)); // [{ id: 1, ... }, { id: 2, ... }]
// Fallback with or
const fallbackUser = or(nonExistentUser, some(defaultUser));
console.log('Fallback user:', unwrapOr(fallbackUser, null)); // { id: 3, name: 'Guest', ... }
// Combine values with liftA2
const combinedNames = liftA2(
(u1: User, u2: User) => `${u1.name} & ${u2.name}`,
user1,
user2
);
console.log('Combined names:', unwrapOr(combinedNames, 'No pair')); // 'Alicia & Bob'
// Match cases with matchOption
const matched = matchOption(
nonExistentUser,
(u) => some(`Found: ${u.name}`),
() => some('No user found')
);
console.log('Matched result:', unwrap(matched)); // 'No user found'
// Map with default value
const nameWithDefault = mapWithDefault(user1, (u) => u.name.toUpperCase(), 'N/A');
console.log('Name with default:', unwrap(nameWithDefault)); // 'ALICIA'
// Delete a user
const deletedUser = deleteUser(1);
console.log('Deleted user:', unwrapOr(deletedUser, null)); // { id: 1, name: 'Alicia', ... }
// Display current database
console.log('Current DB:', userDB); // [{ id: 2, name: 'Bob', ... }]
})();