Tagged errors
Define discriminated Error subclasses with typed properties, guards, JSON output, causes, and generator support.
TaggedError creates real Error subclasses with a literal _tag and typed properties.
Define an error
import { TaggedError } from "better-result";
class UserNotFound extends TaggedError("UserNotFound")<{
userId: string;
message: string;
}> {}
const error = new UserNotFound({
userId: "usr_123",
message: "User usr_123 was not found",
});
The class has normal Error behavior plus:
name === "UserNotFound";_tag === "UserNotFound"as a string literal;- readonly
userIdand other declared properties; toJSON();UserNotFound.is(value);- iterator support for
yield*.
Add a computed message
class RequestFailed extends TaggedError("RequestFailed")<{
url: string;
status: number;
message: string;
}> {
constructor(args: { url: string; status: number }) {
super({
...args,
message: `Request to ${args.url} failed with ${args.status}`,
});
}
}
Preserve a cause
Declare cause in the property type and pass it to super:
class ParseFailed extends TaggedError("ParseFailed")<{
input: string;
cause: unknown;
message: string;
}> {}
new ParseFailed({ input, cause, message: "Could not parse input" });
Native Error.cause is populated. toJSON() serializes an Error cause to its name, message, and stack.
Guards
if (UserNotFound.is(value)) {
value.userId;
}
if (TaggedError.is(value)) {
value._tag;
value.toJSON();
}
if (isTaggedError(value)) {
value._tag;
}
TaggedError.is and isTaggedError detect any better-result tagged error. A concrete class’s .is guard detects that class.
Yield directly
const result = Result.gen(function* () {
if (!user) {
yield* new UserNotFound({ userId, message: "User not found" });
}
return Result.ok(user);
});
Public helper types
TaggedErrorClass<Tag>describes the factory’s generic class.TaggedErrorInstance<Tag, Props>describes an instance structurally.AnyTaggedErrordescribes any tagged error withtoJSON().