TypeScript Error Handling with Discriminated Unions
How can I annotate the type so that based on the input parameter it disambiguate the errors of the effect?
class TestCommand extends Schema.TaggedClass<TestCommand>()(
"commands/test-command",
{
test: Schema.NonEmptyString,
},
) {}
class TestError extends Data.TaggedError("errors/test-error")<{}> {}
const handlerTestCommand = (cmd: typeof TestCommand.Type) => Effect.gen(function*() {
yield* Effect.log(`test command run: ${cmd.test}`)
yield* Effect.fail(new TestError())
})
class DataCommand extends Schema.TaggedClass<DataCommand>()(
"commands/data-command",
{
data: Schema.NonEmptyString,
},
) {}
class DataError extends Data.TaggedError("errors/data-error")<{}> {}
const handlerDataCommand = (cmd: typeof DataCommand.Type) => Effect.gen(function*() {
yield* Effect.log(`data command run: ${cmd.data}`)
yield* Effect.fail(new DataError())
})
const DomainCommand = Schema.Union(TestCommand, DataCommand)
interface AggregateState {
done: boolean;
}
class Aggregate {
constructor(private readonly state: SynchronizedRef.SynchronizedRef<AggregateState>) {}
static make = Effect.andThen(SynchronizedRef.make<AggregateState>({ done: false }), (ref) => new this(ref))
execute(command: typeof DomainCommand.Type) {
return Effect.gen(function*() {
const handler = Match.type<typeof DomainCommand.Type>().pipe(
Match.tag("commands/data-command", handlerDataCommand),
Match.tag("commands/test-command", handlerTestCommand),
Match.exhaustive
)
yield* handler(command)
})
}
}
const program = Effect.gen(function*() {
const myAggregate = yield* Aggregate.make
const testCmd = yield* Schema.decode(TestCommand)({
_tag: "commands/test-command",
test: "test data"
})
// This is Effect.Effect<void, TestError | DataError, never>
// I would like it to be Effect.Effect<void, TestError, never>
yield* myAggregate.execute(testCmd)
})class TestCommand extends Schema.TaggedClass<TestCommand>()(
"commands/test-command",
{
test: Schema.NonEmptyString,
},
) {}
class TestError extends Data.TaggedError("errors/test-error")<{}> {}
const handlerTestCommand = (cmd: typeof TestCommand.Type) => Effect.gen(function*() {
yield* Effect.log(`test command run: ${cmd.test}`)
yield* Effect.fail(new TestError())
})
class DataCommand extends Schema.TaggedClass<DataCommand>()(
"commands/data-command",
{
data: Schema.NonEmptyString,
},
) {}
class DataError extends Data.TaggedError("errors/data-error")<{}> {}
const handlerDataCommand = (cmd: typeof DataCommand.Type) => Effect.gen(function*() {
yield* Effect.log(`data command run: ${cmd.data}`)
yield* Effect.fail(new DataError())
})
const DomainCommand = Schema.Union(TestCommand, DataCommand)
interface AggregateState {
done: boolean;
}
class Aggregate {
constructor(private readonly state: SynchronizedRef.SynchronizedRef<AggregateState>) {}
static make = Effect.andThen(SynchronizedRef.make<AggregateState>({ done: false }), (ref) => new this(ref))
execute(command: typeof DomainCommand.Type) {
return Effect.gen(function*() {
const handler = Match.type<typeof DomainCommand.Type>().pipe(
Match.tag("commands/data-command", handlerDataCommand),
Match.tag("commands/test-command", handlerTestCommand),
Match.exhaustive
)
yield* handler(command)
})
}
}
const program = Effect.gen(function*() {
const myAggregate = yield* Aggregate.make
const testCmd = yield* Schema.decode(TestCommand)({
_tag: "commands/test-command",
test: "test data"
})
// This is Effect.Effect<void, TestError | DataError, never>
// I would like it to be Effect.Effect<void, TestError, never>
yield* myAggregate.execute(testCmd)
})