DEV Community

Cover image for Frontend Architecture. Result Type Pattern
Dmitrii Nefedov
Dmitrii Nefedov

Posted on

Frontend Architecture. Result Type Pattern

Hi everyone! Today I want to talk (or remind) about a really cool and practical pattern called the Result type. I think this article will be short, but interesting for those who haven’t worked with this approach before and are tired of callback hell and unexpected exceptions when developing web applications.

What is the pattern about?

It’s simple — stop using exceptions for normal business logic.

Exceptions are meant for truly exceptional cases, situations that are not expected during normal program execution. But in many applications, we often see business logic built on exceptions, which is actually an anti-pattern. If an error is likely to happen during normal business logic, it is not an exceptional case by definition.

Today, we’ll look at a cleaner alternative from the perspective of business logic — the Result type.

Implementation in TypeScript

I want to show the full implementation first and explain it below. I think this makes it easier to understand.

type SuccessResult<P> = {
  success: true;
  payload: P;
};

type ErrorResult<E> = {
  success: false;
  error: E;
};

export type Result<P, E> = SuccessResult<P> | ErrorResult<E>;

const success = <P>(payload: P): Result<P, never> => {
  return { success: true, payload };
};

const error = <E>(error: E): Result<never, E> => {
  return { success: false, error };
};

export const Result = {
  success,
  error,
};
Enter fullscreen mode Exit fullscreen mode

I created two separate types (SuccessResult and ErrorResult) with different success flags, and a union type Result that we export.

I also made two factory functions to simplify creating Result in its different variations - success and error functions.

enum DOMAIN_ERROR {
  NOT_ENOUGH_BALANCE = "NOT_ENOUGH_BALANCE",
}

type Payload = {
  transactionId: string;
};

const transferMoney = (): Result<Payload, DOMAIN_ERROR> => {
  const random = Math.random();

  if (random > 0.5) {
    // Error: not enough balance
    return Result.error<DOMAIN_ERROR>(DOMAIN_ERROR.NOT_ENOUGH_BALANCE);
  }

  // Success
  return Result.success<Payload>({ transactionId: "tx456" });
};

const transferResult = transferMoney();

if (transferResult.success) {
  console.log(transferResult.payload);
} else {
  console.log(transferResult.error);
}
Enter fullscreen mode Exit fullscreen mode

In this example, we simulate a money transfer. The function transferMoney can either succeed and return a transaction ID, or fail if there is not enough balance. By returning a Result type instead of throwing an exception, we make it explicit which outcome happened.

The if (transferResult.success) check lets TypeScript narrow the type, so inside the if block we know for sure that payload exists, and inside the else block we know that error exists. This makes handling both success and error cases safe and predictable.

Advantages of this approach

  • No exceptions: type safety is always preserved, and there are no hidden cases or unexpected values.
  • TypeScript ensures error handling: without checking success, we cannot access payload. This makes it easier to see where errors may occur and handle them properly.
  • Clear list of errors: you can use enums or more complex types/classes, giving you full control over error information and format.
  • Readable business logic: the flow is obvious, especially useful in large applications.

Conclusion

I really like this pattern and recommend using it based on many years of web development experience. It gives more control over runtime behavior and fits well with architectures like Domain-driven design. Unit testing also becomes simpler and clearer.

Yes, you have to write a bit more code and always check success before accessing payload instead of just throwing an exception, but our job as developers is not to write less code, but to write it well, right?

What do you think — is it worth using this pattern in your projects? Leave your comments :) I would love any feedback!

And thanks for your time 🙏

Top comments (0)