type ErrorFunc<ErrorMap extends Record<string, string>> = <
Kind extends keyof ErrorMap,
Args extends [kind: Kind, ctx?: string]
>(...args: Args) => {
ok: false,
error: Args[1] extends string ? {
kind: Args[0],
message: ErrorMap[Args[0]],
ctx: Args[1]
} : {
kind: Args[0],
message: ErrorMap[Args[0]],
}
};
const defineErrors = <
Key extends string,
Value extends string,
ErrorMap extends Record<Key, Value>
>(errorMap: ErrorMap): ErrorFunc<ErrorMap> => {
return (kind, ctx?: string) => ({
ok: false,
error: {
kind: kind,
message: errorMap[kind],
ctx: ctx
}
});
};
// --- Example
// Define all possible errors
const error = defineErrors({
NOT_FOUND: "user not found",
INVALID_ID: "invalid id"
})
// Trigger the error
const a = error("NOT_FOUND")
const b = error("NOT_FOUND", 'some optional context for the error')
const C = error("NOT_FOUNDD")
// More example: check if error is of specific kind
const err = Math.random() > 0.5 ? error("NOT_FOUND") : error("INVALID_ID")
if (err.error.kind == "NOT_FOUND") {
console.log("encountered 'NOT_FOUND' error")
} else if (err.error.kind == "INVALID_ID") {
console.log("encountered 'NOT_FOUND' error")
}
Last active
December 26, 2022 23:46
-
-
Save wzulfikar/972b3d2efd4bc38a29319e4c9e494ec1 to your computer and use it in GitHub Desktop.
Typescript helper to define errors.
Combine the defineErrors
with Result
type to create result container:
// --- Add "Result" type to create "result" container
type Result<
TOk,
TError extends ReturnType<ErrorFunc<any>>
> = TOk | TError;
type Ok<T> = {
ok: true;
} & T;
function ok<TOk>(result: TOk): Ok<TOk> {
return {
ok: true,
...result
};
}
// --- Example of using result container in a fetch function
type User = {
id: string;
name: string;
}
const userError = defineErrors({
NOT_FOUND: "user not found"
})
type UserResult = Result<Ok<{user: User}>, ReturnType<typeof userError>>;
const fetchUser = (userId: string): Promise<UserResult> =>
fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
.then((res) => res.json())
.then((json) => json.id ? ok({user: json}) : userError("NOT_FOUND"))
const user = fetchUser("1").then((result) => result.ok ? result : result)
With result container, you can see all possible outcomes of the function (eg. "ok" for happy path, error for other) in compile time:
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
defineErrors
provides a way to define all possible errors at compile time:The
error
function produced bydefineErrors
correctly resolves the error at compile time:An optional context message (
ctx
) is also supported:The
error
function also prevents wrong error to be used: