Nominal (branded) TypeScript types & helpers for safe opaque types.
π·οΈπ§©π‘οΈ Type-safe branding for primitives and objects in TypeScript.
See the Usage Guide for practical examples and patterns.
See the API Reference for a summary of all exports and detailed documentation.
pnpm add @variablesoftware/vs-brand-utils
import {
Brand,
brand,
isBrand,
assertBrand,
unbrand,
createBrand,
brandArray,
unbrandArray,
brandMany
} from "@variablesoftware/vs-brand-utils";
// Define a branded type
export type UserId = Brand<"UserId", string>;
// Brand a value
const id = brand<"UserId", string>("UserId", "abc123");
// Type-safe: cannot assign UserId to string or vice versa
// const s: string = id; // Type error
// const id2: UserId = 'plainstring'; // Type error
// Type guard
if (isBrand("UserId", id)) {
// id is UserId here
}
// Assert
assertBrand("UserId", id);
// Unbrand
const raw: string = unbrand("UserId", id);
// Brand an array
const brandedArr = brandArray("UserId", ["a", "b"]);
// Unbrand an array
const arr: string[] = unbrandArray("UserId", brandedArr);
// Brand many values
const many = brandMany("UserId", "a", "b");
// Factory for a specific brand
const TenantId = createBrand<"TenantId", string>("TenantId");
const tid = TenantId.brand("t-123");
- π·οΈ Nominal/opaque types for primitives and objects
- π§© Type-safe branding and unbranding helpers
- π‘οΈ Compile-time enforcement: prevents accidental mixing of branded and unbranded types
- Array helpers for batch branding/unbranding
- Factory for reusable brand helpers
- Zero runtime cost: all checks are type-level
- 100% test and code coverage (see
tests/unit/
for split test suites) - Edge-case and runtime safety for all helpers
- Prevent accidental mixing of IDs, tokens, and other primitives
- Enable safe domain modeling with TypeScript
- No runtime overhead or dependencies
- Simple, composable API
- Fully documented and tested
Tested using vitest
with coverage for:
- Branding and unbranding
- Type guards and assertions
- Compile-time type errors
- Array helpers
- Edge cases and runtime behaviors
Test suites are organized by concern:
runtime-branding.test.ts
compile-type-errors.test.ts
helpers-and-factory.test.ts
edge-cases.test.ts
missing-marker-edge-cases.test.ts
Run tests:
pnpm test
As of May 2025, the codebase is split into focused modules for maintainability and tree-shaking:
core
β core types and internal symbolsbrand
β branding helpers (brand
,brandArray
,brandMany
)typeguards
β type guard helpers (isBrand
)assert
β assertion helpers (assertBrand
)unbrand
β unbranding helpers (unbrand
,unbrandArray
)factory
β thecreateBrand
factory for a bundled API
You can import everything from the main entry point:
import { brand, isBrand, assertBrand, unbrand, createBrand } from "@variablesoftware/vs-brand-utils";
Or, for advanced usage and smaller bundles, import only what you need:
import { brand } from "@variablesoftware/vs-brand-utils/brand";
import { isBrand } from "@variablesoftware/vs-brand-utils/typeguards";
All helpers are still available via the main package import for convenience.
- All helpers and edge cases are tested for 100% coverage.
- See
src/
for the modular implementation. - See
tests/unit/
for split test suites by concern.
This package is under active development and not yet stable.
Once stable, it will be published as:
"@variablesoftware/vs-brand-utils": "^0.8.0"
MIT Β© Rob Friedman / Variable Software
Built with β€οΈ by @variablesoftware
Thank you for downloading and using this project. Pull requests are warmly welcomed!
- Naming, logging, error messages, and tests avoid cultural or ableist bias
- Avoids assumptions about input/output formats or encodings
- Designed for clarity, predictability, and parity with TypeScript best practices
- Works well in diverse, multilingual, and inclusive developer environments