# Advanced TypeScript Error Handling with neverthrow: An E-commerce Example

In this tutorial, we'll explore how to handle errors in a TypeScript application using the `neverthrow` library, focusing on a practical e-commerce scenario. Specifically, we'll write a service to check product availability and place an order, handling potential errors in a type-safe manner similar to Rust's error handling.

## Initial Setup

Let's set up our environment with TypeScript and the necessary domain-driven design elements. First, we'll define the models and interfaces that represent the domain model, and import the necessary packages like `neverthrow` and `luxon` for date handling.

### Importing Required Libraries

```typescript
import { Product } from '@domain/product/Product';
import { InventoryRepositoryInterface } from '@domain/inventory/InventoryRepositoryInterface';
import { Order, OrderDetails } from '@domain/order/Order';
import { OrderRepositoryInterface } from '@domain/order/OrderRepositoryInterface';
import { DateTime } from 'luxon';
import { err, ok, Result } from 'neverthrow';
import { makeTaggedUnion, none, MemberType } from 'safety-match';
```

### Defining Input and Error Types

Next, we define the input for our service and the types of errors that could occur:

```typescript
export interface PlaceOrderInput {
  productId: string;
  quantity: number;
  orderDate: DateTime;
}

export const PlaceOrderError = makeTaggedUnion({
  ProductNotFound: none,
  InsufficientStock: none,
  OrderSaveFailed: none,
});

export type PlaceOrderError = MemberType<typeof PlaceOrderError>;
```

In this setup, `PlaceOrderInput` specifies the information required to place an order, and `PlaceOrderError` defines possible error scenarios.

### Implementing the Service

Now, let's implement the service. We'll use the `neverthrow` library to manage different outcomes of operations explicitly, ensuring that every function call that might fail is clear in its intent and handling.

```typescript
export class OrderService {
  constructor(
    private readonly inventoryRepository: InventoryRepositoryInterface,
    private readonly orderRepository: OrderRepositoryInterface
  ) {}

  async placeOrder(input: PlaceOrderInput): Promise<Result<Order, PlaceOrderError>> {
    // Check product availability
    const stockLevel = await this.inventoryRepository.getStockLevel(input.productId);
    if (stockLevel === null) return err(PlaceOrderError.ProductNotFound);
    if (stockLevel < input.quantity) return err(PlaceOrderError.InsufficientStock);

    // Create and save the order
    const orderDetails: OrderDetails = {
      productId: input.productId,
      quantity: input.quantity,
      orderDate: input.orderDate
    };
    const order = new Order(orderDetails);
    const saveResult = await this.orderRepository.saveOrder(order);

    if (!saveResult) return err(PlaceOrderError.OrderSaveFailed);
    return ok(order);
  }
}
```

#### Explanation of Service Logic:

1. **Check Product Availability**: The service first checks if the product is available and in sufficient quantity. It returns early with an appropriate error if not.
    
2. **Place the Order**: If the product is available, the service creates an order. It attempts to save this order using the order repository.
    
3. **Error Handling**: If saving the order fails, an error is returned. Otherwise, the successfully placed order is returned.
    

## Benefits of This Approach

Using `neverthrow` provides several advantages:

* **Explicit Error Handling**: Each function's outcome, whether success or failure, is clearly defined through its return type.
    
* **Type Safety**: Errors are part of the type system, forcing developers to handle them explicitly, reducing the chance of unhandled exceptions.
    
* **Improved Maintainability**: Having a clear and predictable error handling strategy makes the code easier to maintain and debug.
    

This method aligns with modern best practices in TypeScript application development, where enhancing clarity, type safety, and robustness are key priorities. By structuring your error handling in this detailed manner, your codebase becomes more predictable and less prone to runtime errors.  
To conclude, while this tutorial showcases an approach to structured and type-safe error handling in TypeScript using the `neverthrow` library, I must credit the initial inspiration to my colleague, [Killian](https://www.linkedin.com/in/%F0%9F%96%A5%EF%B8%8F-killian-guibout-chatelain-1623421a2). His insights and innovative ideas significantly influenced the development of these error handling techniques, demonstrating the power of collaboration in software development.
