Bird
Raised Fist0
Typescriptprogramming~5 mins

Branded types for safety in Typescript

Choose your learning style10 modes available
Introduction

Branded types help keep different kinds of data separate even if they look the same. This stops mistakes where you mix things up by accident.

When you have two values that are both strings but mean different things, like UserID and ProductID.
When you want to avoid mixing up similar numbers, like OrderNumber and InvoiceNumber.
When you want the computer to catch errors early by making types more specific.
When you want to add extra meaning to simple types without changing their runtime behavior.
Syntax
Typescript
type Brand<K, T> = K & { __brand: T };

// Example:
type UserID = Brand<string, 'UserID'>;
type ProductID = Brand<string, 'ProductID'>;

The Brand type combines a base type K with a unique tag T to create a new type.

This tag __brand does not exist at runtime; it only helps TypeScript check types.

Examples
This shows two branded types that both use strings but are treated differently by TypeScript.
Typescript
type UserID = Brand<string, 'UserID'>;
type ProductID = Brand<string, 'ProductID'>;

const userId: UserID = 'abc123' as UserID;
const productId: ProductID = 'abc123' as ProductID;

// Error if you try to assign one to the other:
// const wrong: UserID = productId;
Function expects a branded UserID, so you must cast or create the right type to call it.
Typescript
function getUser(id: UserID) {
  console.log(`User ID is ${id}`);
}

const id = 'user-1' as UserID;
getUser(id);
Sample Program

This program defines two branded types and a function that only accepts UserID. It shows how TypeScript prevents mixing them up.

Typescript
type Brand<K, T> = K & { __brand: T };

type UserID = Brand<string, 'UserID'>;
type ProductID = Brand<string, 'ProductID'>;

function printUserId(id: UserID) {
  console.log(`User ID: ${id}`);
}

const userId = 'user123' as UserID;
const productId = 'prod123' as ProductID;

printUserId(userId);

// The next line would cause a TypeScript error if uncommented:
// printUserId(productId);
OutputSuccess
Important Notes

Branded types only exist during development and do not add code at runtime.

You must use as to tell TypeScript when you want to treat a value as a branded type.

This technique helps catch bugs early by making types more specific.

Summary

Branded types add a hidden tag to simple types to keep them distinct.

They help avoid mixing up similar data like IDs or codes.

They improve safety without changing runtime behavior.

Practice

(1/5)
1. What is the main purpose of using branded types in TypeScript?
easy
A. To add new methods to primitive types
B. To create distinct types from simple types for better type safety
C. To improve runtime performance of the code
D. To enable automatic type conversion between types

Solution

  1. Step 1: Understand branded types concept

    Branded types add a unique tag to simple types like string or number to keep them distinct in the type system.
  2. Step 2: Identify the purpose of branding

    This helps avoid mixing similar data types (like user IDs and product IDs) by making them incompatible even if they share the same base type.
  3. Final Answer:

    To create distinct types from simple types for better type safety -> Option B
  4. Quick Check:

    Branded types = distinct types for safety [OK]
Hint: Branded types prevent mixing similar types safely [OK]
Common Mistakes:
  • Thinking branded types improve runtime speed
  • Confusing branded types with adding methods
  • Assuming branded types convert types automatically
2. Which of the following is the correct way to define a branded type for a string called UserId?
easy
A. type UserId = string | { __brand: 'UserId' }
B. type UserId = { __brand: 'UserId' }
C. type UserId = string & { __brand: 'UserId' }
D. type UserId = string extends { __brand: 'UserId' }

Solution

  1. Step 1: Recall branded type syntax

    Branded types use intersection (&) to combine a base type with a unique tag object.
  2. Step 2: Check each option

    type UserId = string & { __brand: 'UserId' } correctly uses string & { __brand: 'UserId' }. type UserId = string | { __brand: 'UserId' } uses union (|) which allows either type, not branding. type UserId = { __brand: 'UserId' } misses base type. type UserId = string extends { __brand: 'UserId' } is invalid syntax.
  3. Final Answer:

    type UserId = string & { __brand: 'UserId' } -> Option C
  4. Quick Check:

    Branded type = base & unique tag [OK]
Hint: Use & intersection to add brand to base type [OK]
Common Mistakes:
  • Using union (|) instead of intersection (&)
  • Omitting the base type in branded type
  • Writing invalid TypeScript syntax
3. Given the code below, what will be the output?
type UserId = string & { __brand: 'UserId' };
const id1 = 'abc123' as UserId;
const id2 = 'xyz789' as UserId;

function isSameUser(a: UserId, b: UserId) {
  return a === b;
}

console.log(isSameUser(id1, id2));
medium
A. false
B. Runtime error
C. Type error at compile time
D. true

Solution

  1. Step 1: Understand branded types at runtime

    Branded types only exist at compile time for safety. At runtime, they are just strings.
  2. Step 2: Evaluate the comparison

    Since id1 is 'abc123' and id2 is 'xyz789', the comparison a === b returns false.
  3. Final Answer:

    false -> Option A
  4. Quick Check:

    Branded types = strings at runtime, so 'abc123' === 'xyz789' is false [OK]
Hint: Branded types are strings at runtime; compare values normally [OK]
Common Mistakes:
  • Expecting branded types to cause runtime errors
  • Thinking branded types add runtime checks
  • Assuming same brand means always equal
4. Identify the error in this code using branded types:
type ProductId = string & { __brand: 'ProductId' };
function getProduct(id: ProductId) {
  return `Product: ${id}`;
}

const id = '123' as string;
getProduct(id);
medium
A. Argument of type 'string' is not assignable to parameter of type 'ProductId'
B. No error, code works fine
C. Function getProduct returns wrong type
D. Branding syntax is incorrect

Solution

  1. Step 1: Check function parameter type

    getProduct expects a ProductId, which is a branded string type.
  2. Step 2: Analyze argument passed

    The argument id is typed as plain string, not branded. TypeScript disallows passing plain string where branded type is expected.
  3. Final Answer:

    Argument of type 'string' is not assignable to parameter of type 'ProductId' -> Option A
  4. Quick Check:

    Branded type requires explicit cast or branded value [OK]
Hint: Pass branded type, not plain string, to branded parameters [OK]
Common Mistakes:
  • Passing plain string without branding
  • Ignoring TypeScript type errors
  • Assuming branding affects runtime
5. You have two branded types:
type UserId = string & { __brand: 'UserId' };
type OrderId = string & { __brand: 'OrderId' };
How can you safely create a function that accepts either UserId or OrderId but not plain strings, and returns a string description?
hard
A. function describeId(id: unknown) { return `ID: ${id}`; }
B. function describeId(id: string) { return `ID: ${id}`; }
C. function describeId(id: UserId & OrderId) { return `ID: ${id}`; }
D. function describeId(id: UserId | OrderId) { return `ID: ${id}`; }

Solution

  1. Step 1: Understand union of branded types

    Using UserId | OrderId allows the function to accept either branded type but excludes plain strings.
  2. Step 2: Check other options

    function describeId(id: string) { return `ID: ${id}`; } accepts plain strings, which is unsafe. function describeId(id: UserId & OrderId) { return `ID: ${id}`; } requires a value that is both brands simultaneously, which is impossible. function describeId(id: unknown) { return `ID: ${id}`; } accepts any type, losing type safety.
  3. Final Answer:

    function describeId(id: UserId | OrderId) { return `ID: ${id}`; } -> Option D
  4. Quick Check:

    Use union of branded types to accept multiple safe types [OK]
Hint: Use union (|) of branded types to accept multiple safe IDs [OK]
Common Mistakes:
  • Using intersection (&) instead of union (|)
  • Accepting plain strings losing safety
  • Using unknown type losing type checks
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard