Carbonteq
Best Practices/Backend

Branded Types

Learn why branded types matter and how to implement them properly in TypeScript

Building Type-Safe Applications with Branded Types

This tutorial demonstrates the evolution from unsafe TypeScript patterns to robust branded types using @carbonteq/refined-type.

1
The Problem: Type Safety Illusion

TypeScript uses structural typing, meaning types with the same structure are interchangeable. This can cause bugs when different concepts share the same underlying type.

2
Approach 1: Interface Branding

The first approach uses interfaces with unique symbols to create distinct types.

3
Approach 2: Generic Brand Helper

The second approach uses a generic Brand type to create opaque types more easily.

4
Approach 3: Class-based Wrappers

The third approach uses classes to wrap values and provide validation, but has drawbacks.

5
The Solution: refined-type Library

@carbonteq/refined-type combines the best of all approaches: compile-time type safety, runtime validation, and functional error handling.

6
Safe Creation and Error Handling

The Result-based API eliminates exceptions and provides elegant error handling patterns.

7
Custom Error Types and Domain Logic

Create domain-specific error types for better error handling and business logic representation.

8
Working with Primitive Values

Access underlying primitive values when needed for calculations or external APIs.

9
Complex Object Validation

Compose refined types into complex data structures for comprehensive validation.

type UserId = number;
type ProductId = number;
const getUserById = (id: UserId) => {
console.log(`Getting user ${id}`);
};
const getProductById = (id: ProductId) => {
console.log(`Getting product ${id}`);
};
const userId: UserId = 123;
const productId: ProductId = 456;
// This compiles but is logically wrong!
getUserById(productId);
Wrong ID type passed to function
getProductById(userId);
Different ID types are interchangeable

Key Benefits

This tutorial demonstrates how @carbonteq/refined-type solves the fundamental problems with TypeScript's structural typing:

  • 🔒 Type Safety: True nominal types prevent accidental mixing
  • ✅ Runtime Validation: Zod integration ensures data integrity
  • 🚫 No Exceptions: Result-based API eliminates error-prone try/catch
  • 🎯 Domain Modeling: Custom errors represent business logic
  • ⚡ Performance: Zero overhead after validation
  • 🔧 Composable: Chain and combine validations elegantly

The progression from basic TypeScript pitfalls to robust branded types shows why proper validation and type safety are essential for production applications.