Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
TypeScriptJavaScriptJune 23, 20264 min read

TypeScript Branded Types for Preventing Silent Data Loss

TypeScript Branded Types help you prevent silent data loss by enforcing strict ID validation at compile-time, moving beyond simple primitive types.

TypeScriptType SafetyDomain-Driven DesignDefensive ProgrammingBranded TypesJavaScriptFrontend

Last month, I spent about four hours debugging a production issue where an OrderId was accidentally passed into a function expecting a UserId. Both were just plain strings, so the compiler stayed silent while the database returned an empty result, causing a silent failure in our checkout flow.

If you’ve ever felt the pain of primitive obsession—where everything is just a string or a number—you know how easily data can get mixed up. Using TypeScript Branded Types for Domain-Specific ID Validation is the most effective way I've found to stop this class of bug before it ever hits your test suite.

The Problem with Primitive Obsession

In a standard Node.js project, we often define our entities using simple types:

TYPESCRIPT
type UserId = string;
type OrderId = string;

function processOrder(orderId: OrderId, userId: UserId) {
  // Logic here
}

Because OrderId and UserId are just aliases for string, TypeScript treats them as identical. You can pass a UserId into the orderId parameter, and the compiler won't blink. This is a classic case of primitive obsession that TypeScript Value Objects: Eliminating Primitive Obsession in Your Code usually solves, but sometimes you need a zero-overhead solution that doesn't require runtime object instantiation.

Implementing Branded Types

A branded type (or opaque type) adds a "tag" to a primitive, making it structurally unique to the TypeScript compiler while keeping it a plain primitive at runtime.

Here is how I implement them:

TYPESCRIPT
type Branded<T, B> = T & { __brand: B };

type UserId = Branded<string, CE9178">'UserId'>;
type OrderId = Branded<string, CE9178">'OrderId'>;

function processOrder(orderId: OrderId, userId: UserId) {
  console.log(CE9178">`Processing ${orderId} for ${userId}`);
}

const myOrder = CE9178">'order_123' as OrderId;
const myUser = CE9178">'user_456' as UserId;

processOrder(myOrder, myUser); // Works!
processOrder(myUser, myOrder); // Error: Type CE9178">'UserId' is not assignable to CE9178">'OrderId'

The __brand property doesn't exist at runtime, so there is no performance penalty. It's strictly a compile-time construct that enforces Domain-Driven Design principles without the ceremony of full-blown classes.

Why This is Defensive Programming

When you adopt this pattern, you are effectively creating a contract that the compiler enforces. I’ve found that using TypeScript Branded Types makes my functions self-documenting. If a function signature requires a ProductId, I don't have to guess if I should pass a SKU or a database UUID; the type system forces me to pass the correct brand.

If you are already using TypeScript Zod Schema Validation: A Guide to Runtime Type Safety, you can combine these techniques to ensure that once your data passes the schema validation, it is "branded" correctly for the rest of your application.

The Trade-offs

I’ll be honest: there’s a small developer experience tax here. You have to explicitly cast your data when it enters your system.

TYPESCRIPT
function toUserId(id: string): UserId {
  return id as UserId;
}

You'll need a helper function or a validation step at your API boundaries to perform this cast. If you don't do this, you'll end up littering your codebase with as UserId assertions, which defeats the purpose of being type-safe.

When to Use Them

I don't use branded types for every single string. That would be overkill. I reserve them for:

  1. IDs: Any identifier that shouldn't be mixed up with another.
  2. Currency/Units: Distinguishing between USD and EUR, or Pixels and Rem.
  3. Status Enums: Ensuring that an OrderStatus isn't accidentally passed where a PaymentStatus is expected.

I’ve found that applying this to IDs alone prevents roughly 80% of the "wrong ID" bugs I see in PR reviews.

Common Questions

Does this add overhead to my JavaScript bundle? No. Since the __brand property is never actually assigned to the object, it is completely erased by the TypeScript compiler. Your runtime code remains pure JavaScript.

Can I use this with numbers? Absolutely. It works perfectly with number types. It's particularly useful for distinguishing between different kinds of IDs, like InternalId vs ExternalId, or Timestamp vs Duration.

Should I use this for everything? No. Over-using it can make your code harder to read. Use it for domain boundaries and critical identifiers where the cost of a mismatch is high.

Closing Thoughts

Moving toward strict type safety is a journey, not a destination. While branded types are a powerful tool for Defensive Programming, they aren't a silver bullet. You still need unit tests to verify your logic.

Next time, I'm thinking about exploring how these types play with complex data transformers, perhaps looking at TypeScript Conditional Types for Smarter, Self-Documenting Data Transformers to automate the branding process. For now, try adding a brand to your next UserId and see how many hidden assumptions it uncovers in your current code. It's usually more than you expect.

Back to Blog

Similar Posts

Close-up of a vintage typewriter with paper displaying 'Domain Search' text, ideal for retro themes.
TypeScriptJavaScriptJune 21, 20264 min read

TypeScript Branded Types: Enforcing Domain Integrity at Compile-Time

TypeScript branded types provide a powerful way to enforce domain integrity. Learn how to implement opaque types to prevent bugs and improve code safety.

Read more
TypeScript
JavaScript
June 23, 2026
4 min read

Type-Safe Pipelines: Mastering Advanced TypeScript Transformations

Learn to build Type-Safe Pipelines using TypeScript variadic tuple types and recursive mapped types to catch transformation errors at compile-time.

Read more
TypeScriptJavaScriptJune 23, 20265 min read

TypeScript Recursive Conditional Types for Nested Finite State Machines

TypeScript recursive conditional types allow you to build bulletproof finite state machines. Learn how to enforce nested workflow validation at compile-time.

Read more