pb.safe
v1.0.0
Published
Errors as values; try/catch as a function.
Downloads
105
Readme
safe
Installation
npm install pb.safe
Requirements
typescript@>=5.0.0
tsconfig.json > "compilerOptions" > { "strict": true }
API
safe(callback)
Use safe
to execute a given callback
function and return either its result
value or any Error
that it may have thrown. If a non-Error
is thrown, a new
Error
will instantiated with the thrown value as its cause
.
@description
Executes the given
callback
within a try/catch.
- Returns thrown
Error
values as-is if it is aninstanceof
Error
,- Returns thrown non-
Error
values as thecause
of a newError
value.- Otherwise returns the result value as-is.
If the return type of the
callback
isunknown
, then the placeholderUnknown
type is used instead to allow for a return type union containingError
.
import { safe } from "pb.safe";
const value = safe(() => 0 / 0);
// ^ number | Error
void function (): string | Error {
// handle error
if (value instanceof Error) {
return value;
// ^ Error
}
// continue
return value.toString();
// ^ number
};
Promise
return values are also supported.
const value = safe(() => fetch("https://example.com/api/endpoint.json"));
// ^ Promise<Response | Error>
type Value = { id: string };
void function getValue(): Value | undefined | Error {
const valueJson = window.localStorage.getItem("key");
if (!valueJson) {
return undefined;
}
const valueUnknown = safe(() => JSON.parse(valueJson));
if (valueUnknown instanceof Error) {
return valueUnknown;
}
const value = safe(() => parseValueOrThrow(valueUnknown));
if (value instanceof Error) {
return value;
}
return value;
};
unwrap(value)
Use unwrap
in cases where you either want a value or undefined
if Error
.
@description
- Returns
undefined
if the givenvalue
is aninstanceof
Error
.- Otherwise returns the given
value
as is.
import { unwrap } from "pb.safe";
const value = safe(() => 0 / 0);
// ^ number | Error
const valueOrUndefined = unwrap(value);
// ^ number | undefined
ErrorADT
- type
ErrorADT<TType>
- value
ErrorADT(type, cause?)
Use ErrorADT
to define Error
objects with a typed "type" property
instead of sub-classing Error
. The "type" can be used for handling different
Error
cases.
@description
Returns an extended
Error
type with an addedtype
property to use as a discriminated union. Ifundefined
is used as member of theTType
parameter, then thetype
property becomes optional. This allows untypedError
s to be returned alongsideErrorADT
s.
function getStoredValue(): string | undefined | ErrorADT<"Storage"> {
const response = safe(() => window.localStorage.getItem("key"));
if (response instanceof Error) {
return ErrorADT("Storage", response);
}
return response ?? undefined;
}
const value = getStoredValue();
// ^ string | undefined | ErrorADT<"Storage">
if (value instanceof Error) {
//^ Error<"Storage">
if (value.type === "Storage") {
// ^ "Storage"
}
}
import { ErrorADT } from "pb.safe";
type User = { id: string; name: string };
type AuthContext = { isAdmin: boolean };
//prettier-ignore
async function getUser(
id: string,
authContext: AuthContext,
): Promise<User | ErrorADT<"NotFound" | "NotAllowed" | undefined>> {
if (!authContext.isAdmin) {
return ErrorADT("NotAllowed");
}
const user = await safe(() => queryUserFromDatabase(id));
if (user instanceof Error) {
return user;
}
if (!user) {
return ErrorADT("NotFound");
}
return user;
}
export async function onRequest(
params: { id: string },
authContext: AuthContext,
): Promise<Response> {
const user = await getUser(params.id, authContext);
if (user instanceof Error) {
if (user.type === "NotAllowed") {
return Response.json({ error: user.type }, { status: 403 });
} else if (user.type === "NotFound") {
return Response.json({ error: user.type }, { status: 404 });
}
console.error(user);
return Response.json({ error: "InternalServerError" }, { status: 500 });
}
return Response.json({ ...user });
}