Advanced programmer? Read this instead.
Welcome to neverever! This is a TypeScript library that helps you write safer and cleaner code by handling situations where data might be missing or operations might fail. It’s perfect for beginners because it reduces confusing errors like "undefined is not a function" or messy try-catch blocks. neverever is easy to use, works with both regular and asynchronous (Promise-based) code, and has no dependencies, so it won’t bloat your project.
Imagine you’re writing code that looks up a user’s name, but sometimes the name isn’t there (it’s null or undefined). Or maybe you’re fetching data from a server, and the request might fail. Normally, you’d write lots of if checks or try-catch blocks to handle these cases, which can make your code messy. neverever gives you two main tools to make this easier:
- Option: Helps you deal with values that might be missing, like a user’s name that could be
null. - Result: Helps you handle operations that might succeed (e.g., fetching data) or fail (e.g., server error).
These tools come in two flavors:
- Synchronous (
OptionandResult): For regular code that runs immediately. - Asynchronous (
OptionAsyncandResultAsync): For code that waits for Promises, like fetching data from a server.
To start using neverever, install it with npm (or pnpm/yarn):
npm install nevereverYou’ll need TypeScript in your project. If you don’t have a tsconfig.json file, create one with these settings for the best experience:
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node"
}
}Then, import the tools you need in your TypeScript file:
import { Option, Result } from 'neverever';An Option is a way to represent a value that might or might not exist. Think of it like a box:
- Some: The box contains a value, like a number or string.
- None: The box is empty (no value).
This helps you avoid errors when a value might be null or undefined. Instead of checking if (value !== null), you use Option to handle it safely.
Suppose you have a function that gets a user’s name, but it might return null:
import { Option } from 'neverever';
function getUserName(userId: number): string | null {
if (userId === 1) return "Alice";
return null;
}
// Without Option, you need checks
const name = getUserName(999);
if (name === null) {
console.log("No name found"); // No name found
} else {
console.log(name);
}
// With Option, it’s simpler
const optName = Option.from(getUserName(999));
console.log(optName.unwrapOr("No name found")); // No name found
console.log(Option.from(getUserName(1)).unwrapOr("No name found")); // AliceHere, Option.from turns a null or undefined value into None, and a real value into Some. The unwrapOr method gives you the value if it’s Some, or a default if it’s None.
OptionAsync is like Option, but for code that uses Promises (e.g., fetching data from a server). It’s still a box that’s either Some (has a value) or None (empty), but it waits for the Promise to resolve.
Suppose you fetch a user’s email, but the server might return null:
import { OptionAsync } from 'neverever';
async function fetchEmail(userId: number): Promise<string | null> {
// Simulate fetching data
return userId === 1 ? "alice@example.com" : null;
}
const email = await OptionAsync.from(fetchEmail(999)).unwrapOr("No email");
console.log(email); // No email
console.log(await OptionAsync.from(fetchEmail(1)).unwrapOr("No email")); // alice@example.comOptionAsync.from handles the Promise and checks if the result is null, making it None if so. You use await with unwrapOr to get the value or a default.
A Result represents an operation that can either succeed or fail:
- Ok: The operation worked and has a value (e.g., data from a server).
- Err: The operation failed and has an error message.
This is great for avoiding try-catch blocks when something might go wrong.
Suppose you want to parse some JSON, which might fail if the JSON is invalid:
import { Result } from 'neverever';
function parseJson(data: string): Result<any, string> {
return Result.try(
() => JSON.parse(data),
(e) => `Parse error: ${e}`
);
}
const valid = parseJson('{"name": "Bob"}');
console.log(valid.unwrapOr({})); // { name: "Bob" }
const invalid = parseJson('invalid');
console.log(invalid.unwrapOr({})); // {}Result.try runs the function and returns Ok if it works or Err if it throws an error. unwrapOr gives you the successful value or a default.
ResultAsync is like Result, but for asynchronous operations that return Promises. It’s either Ok (success) or Err (failure), but it waits for the Promise.
Suppose you fetch data from a server, which might fail:
import { ResultAsync } from 'neverever';
async function fetchData(id: number): Promise<ResultAsync<any, string>> {
return ResultAsync.try(
async () => (await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)).json(),
() => "Fetch failed"
);
}
const data = await fetchData(1);
console.log(await data.unwrapOr({ title: "No data" })); // { id: 1, title: ..., ... }
const failed = await fetchData(999);
console.log(await failed.unwrapOr({ title: "No data" })); // { title: "No data" }ResultAsync.try handles the async function, returning Ok for success or Err for failure. You use await with unwrapOr to get the value or a default.
- Fewer Errors: No more
nullorundefinedsurprises.OptionandResultmake it clear when something might be missing or fail. - Cleaner Code: Avoid repetitive
ifchecks and try-catch blocks. Methods likemapandunwrapOrsimplify your logic. - Works with Async:
OptionAsyncandResultAsyncmake it easy to handle Promises without messy error handling. - Beginner-Friendly: The library is straightforward, with simple methods to get started.
Try this example to see neverever in action:
import { OptionAsync, ResultAsync } from 'neverever';
// Simulate fetching a user’s profile
async function fetchProfile(userId: number): Promise<ResultAsync<{ name: string }, string>> {
return ResultAsync.try(
async () => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
if (!response.ok) throw new Error("User not found");
return response.json();
},
() => "Failed to fetch profile"
);
}
// Get the user’s name or a default
async function getName(userId: number): Promise<string> {
const profile = await fetchProfile(userId);
return profile.map(user => user.name).unwrapOr("Unknown user");
}
console.log(await getName(1)); // "Leanne Graham"
console.log(await getName(999)); // "Unknown user"This code safely fetches a user’s profile and extracts the name, handling missing data and errors without if checks or try-catch.
Here are some key methods you’ll use a lot:
-
Option/OptionAsync:
unwrapOr(default): Get the value or a default if missing.map(fn): Transform the value (e.g., double a number).filter(predicate): Keep the value only if it meets a condition.toResult(error): Turn anOptioninto aResultwith an error forNone.
-
Result/ResultAsync:
unwrapOr(default): Get the success value or a default if it’s an error.map(fn): Transform the success value.mapErr(fn): Transform the error message.toOption(): Turn aResultinto anOption, ignoring the error.
- Start Small: Try
Option.fromto handlenullvalues in your code, like user inputs or API responses. - Use
Resultfor Errors: Replace try-catch withResult.tryfor functions that might fail, like parsing or calculations. - Practice with Async: Use
ResultAsync.tryfor API calls to avoid complex error handling. - Read Errors: TypeScript will guide you if you use
OptionorResultincorrectly, making it easier to learn.
To learn more, generate detailed API docs with:
npx typedoc --entryPoints src --out docsThis creates a website with full details on every method.
Want to help improve neverever? Here’s how:
- Fork the repository on GitHub.
- Create a branch (
git checkout -b my-fix). - Make changes and commit (
git commit -m 'Fixed a bug'). - Push your branch (
git push origin my-fix). - Open a pull request.
MIT License. See LICENSE for details. Made with ❤️ by Mohammadreza Hajianpour