Introduction

safeids

A tiny TypeScript library that turns your plain string IDs into distinct branded types — so passing a userId where an orderId belongs is a compile error, not a production bug.

Zero runtime overhead. Brands are TypeScript-only constructs. They compile away completely — your bundle is identical to using plain strings.

The problem

TypeScript treats all string IDs as interchangeable. This compiles without error:

function getOrder(userId: string, orderId: string) {
  return db.orders.findOne({ id: userId }) // oops — wrong ID, no error
}

getOrder(order.id, user.id) // args swapped — still compiles ✓

The bug ships. It fails silently at runtime.

The solution

safeids gives each ID type a unique brand. TypeScript now treats UserId and OrderId as incompatible — even though both are strings at runtime.

import { brand, createId } from 'safeids'

type UserId  = brand<string, 'UserId'>
type OrderId = brand<string, 'OrderId'>

function getOrder(userId: UserId, orderId: OrderId) {
  return db.orders.findOne({ id: orderId }) // forced to use the right ID
}

const userId  = createId<UserId>('usr')
const orderId = createId<OrderId>('ord')

getOrder(orderId, userId)
//       ^^^^^^^ Error: Argument of type 'OrderId' is not assignable to 'UserId'

Quick start