Yieldable Errors
Effect's custom error values can be yielded inside TypeScript generators. In Effeck, signal those same domain failures with throw; function* produces values rather than sequencing an Effect program.
Defining an error without a tag
class EmptyDocument extends Data.Error<{
readonly documentId: string
}> {}
function requireText(documentId: string, text: string) {
if (text.trim() === "") throw new EmptyDocument({ documentId })
return text
}
// string!EmptyDocument!Data.Error creates an error class with typed fields and Effect's yieldable behavior. Effeck does not require yielding it: an explicit throw puts EmptyDocument in E. Creating or returning an error object by itself does not fail the computation.
function emptyDocumentError(documentId: string) {
return new EmptyDocument({ documentId })
}
function rejectDocument(documentId: string) {
const error = emptyDocumentError(documentId) // data, not a failure
return error() // calling the error fails with it
}
// never!EmptyDocument!new only constructs, and a call returning an error instance returns data. Calling the held error runs its failure behavior, just as throw error does.
Creating tagged error classes
error EmptyTitle
error TitleTooLong {
length: number
}
function title(text: string) {
if (text.length === 0) throw new EmptyTitle()
if (text.length > 80) throw new TitleTooLong({ length: text.length })
return text
}
// string!EmptyTitle | TitleTooLong!An error declaration is a class that extends the global TaggedError, Effect's Data.TaggedError, which needs no import. Every instance has a readonly _tag equal to the class name, and the class names the instance type as well as constructing values. Use Schema.TaggedError when an error also needs schema-based serialization.
Choose distinct tags for distinct error cases. Tag-based matching compares strings, so two classes with the same tag cannot be distinguished by catchTag or _tag narrowing; do not assume a duplicate tag is diagnosed. is and match test the class with instanceof, so they do tell such classes apart.
Recovering failures by tag
function displayTitle(text: string) {
return Effect.catchTag(title(text), "EmptyTitle", () => "Untitled")
}
// string!TitleTooLong!
function editableTitle(text: string) {
try {
return title(text)
} catch (error) {
if (error is EmptyTitle) return "Untitled"
throw error
}
}
// string!TitleTooLong!Both versions remove EmptyTitle from E while preserving TitleTooLong. Effect.catchTags supports one handler per tag when several cases need different responses. These remain typed failures even though the error classes also support generators in TypeScript.
Coming from Effect
Existing .ts functions that yield Data.Error or Data.TaggedError stay in TypeScript and are called normally from .effeck files. Replace return yield* new ErrorCase(...) with throw new ErrorCase(...) when writing the Effeck implementation.
An Effeck function* is a producer that yields elements. It converts to an Effect Stream wherever one is expected; it is not a place to paste Effect.gen or yield* error sequencing.