npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@digitaldefiance/branded-interface

v0.0.5

Published

Runtime-identifiable interface-like types for TypeScript with zero runtime overhead

Readme

@digitaldefiance/branded-interface

npm version License: MIT

Runtime-identifiable interface-like types for TypeScript with zero runtime overhead.

Why branded-interface?

Standard TypeScript interfaces are erased at compile time, making it impossible to determine which interface a plain object conforms to at runtime. This becomes problematic in large codebases where multiple modules share similar object shapes but need distinct runtime identity.

branded-interface solves this by:

  • Creating interface definitions with embedded metadata for runtime identification
  • Providing type guards to check if a value is a branded instance of a specific interface
  • Maintaining a global registry to track all branded interfaces, primitives, and opaque types across bundles
  • Keeping metadata in non-enumerable Symbol properties for zero serialization overhead

Installation

npm install @digitaldefiance/branded-interface
# or
yarn add @digitaldefiance/branded-interface
# or
pnpm add @digitaldefiance/branded-interface

Quick Start

import {
  createBrandedInterface,
  createBrandedPrimitive,
  isOfInterface,
} from '@digitaldefiance/branded-interface';

// Define a branded interface with a schema
const User = createBrandedInterface<{
  name: string;
  email: string;
  age: number;
}>('User', {
  name:  { type: 'string' },
  email: { type: 'string' },
  age:   { type: 'number' },
});

// Create a validated, frozen branded instance
const user = User.create({ name: 'Alice', email: '[email protected]', age: 30 });

console.log(user.name);  // 'Alice'
console.log(user.email); // '[email protected]'

// Type guard with runtime identification
if (isOfInterface(user, User)) {
  // user is narrowed to BrandedInstance<{ name: string; email: string; age: number }>
  console.log('Valid user:', user.name);
}

// Metadata is invisible to serialization
JSON.stringify(user); // '{"name":"Alice","email":"[email protected]","age":30}'
Object.keys(user);    // ['name', 'email', 'age']

Features

Branded Interfaces

Define structured types with schema validation and runtime identity:

import { createBrandedInterface, isOfInterface, assertOfInterface } from '@digitaldefiance/branded-interface';

const Address = createBrandedInterface<{
  street: string;
  city: string;
  zip: string;
}>('Address', {
  street: { type: 'string' },
  city:   { type: 'string' },
  zip:    { type: 'string', validate: (v) => /^\d{5}(-\d{4})?$/.test(v as string) },
});

const addr = Address.create({ street: '742 Evergreen Terrace', city: 'Springfield', zip: '62704' });

// Type guard
isOfInterface(addr, Address); // true

// Assertion (throws on invalid)
const validated = assertOfInterface(someValue, Address);

Branded Primitives

Constrained primitive types with custom validation:

import { createBrandedPrimitive } from '@digitaldefiance/branded-interface';

const Email = createBrandedPrimitive<string>('Email', 'string', (v) =>
  /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)
);

const PositiveInt = createBrandedPrimitive<number>('PositiveInt', 'number', (v) =>
  Number.isInteger(v) && v > 0
);

Email.create('[email protected]');    // OK
Email.validate('not-an-email');      // false
PositiveInt.create(42);              // OK
PositiveInt.create(-1);              // throws

Branded Enums

Runtime-identifiable enum-like objects (minimal support for use as field refs):

import { createBrandedEnum } from '@digitaldefiance/branded-interface';

const BloodType = createBrandedEnum('BloodType', {
  APos: 'A+', ANeg: 'A-',
  BPos: 'B+', BNeg: 'B-',
  OPos: 'O+', ONeg: 'O-',
} as const);

// Use as a field ref in interface schemas
const Patient = createBrandedInterface('Patient', {
  name:      { type: 'string' },
  bloodType: { type: 'branded-enum', ref: 'BloodType' },
});

Cross-Reference Validation

Interface schemas can reference branded enums, primitives, and other interfaces for cross-validation:

const PhoneNumber = createBrandedPrimitive<string>('PhoneNumber', 'string', (v) =>
  /^\(\d{3}\)\s?\d{3}-\d{4}$/.test(v)
);

const EmergencyContact = createBrandedInterface('EmergencyContact', {
  name:  { type: 'string' },
  phone: { type: 'branded-primitive', ref: 'PhoneNumber' },
});

// phone field is validated against the PhoneNumber primitive
EmergencyContact.create({ name: 'Jane', phone: '(555) 123-4567' }); // OK
EmergencyContact.create({ name: 'Jane', phone: 'bad' });            // throws

Safe Parsing

Parse values without throwing:

import { safeParseInterface } from '@digitaldefiance/branded-interface';

const result = safeParseInterface(unknownData, User);
if (result.success) {
  console.log('Valid:', result.value.name);
} else {
  console.log('Error:', result.error.message);
  console.log('Code:', result.error.code);
  // Codes: 'INVALID_DEFINITION' | 'INVALID_VALUE_TYPE' | 'FIELD_VALIDATION_FAILED'
}

Composition

Merge, extend, pick, omit, and make partial interfaces:

import {
  composeInterfaces,
  extendInterface,
  partialInterface,
  pickFields,
  omitFields,
} from '@digitaldefiance/branded-interface';

const PersonalInfo = createBrandedInterface('PersonalInfo', {
  firstName: { type: 'string' },
  lastName:  { type: 'string' },
});

const InsuranceInfo = createBrandedInterface('InsuranceInfo', {
  provider:  { type: 'string' },
  policyNum: { type: 'string' },
});

// Merge multiple interfaces
const PatientCore = composeInterfaces('PatientCore', PersonalInfo, InsuranceInfo);

// Extend with additional fields
const Patient = extendInterface(PatientCore, 'Patient', {
  mrn: { type: 'string' },
});

// Make all fields optional
const PartialPatient = partialInterface(Patient, 'PartialPatient');

// Pick specific fields
const PatientName = pickFields(PersonalInfo, 'PatientName', ['firstName', 'lastName']);

// Omit specific fields
const NoPolicy = omitFields(InsuranceInfo, 'NoPolicy', ['policyNum']);

Builder Pattern

Fluent API for constructing interface definitions:

import { createBuilder } from '@digitaldefiance/branded-interface';

const LabResult = createBuilder('LabResult')
  .field('testName', { type: 'string' })
  .field('value', { type: 'number' })
  .field('unit', { type: 'string' })
  .optional('notes', { type: 'string' })
  .build();

Opaque Types

Hide underlying values from accidental exposure:

import { createOpaqueType } from '@digitaldefiance/branded-interface';

const OpaqueSSN = createOpaqueType<string>('OpaqueSSN', 'string');

const wrapped = OpaqueSSN.wrap('123-45-6789');
JSON.stringify(wrapped);           // '{}' — value is hidden
Object.keys(wrapped as object);    // [] — nothing visible

const revealed = OpaqueSSN.unwrap(wrapped); // '123-45-6789'
OpaqueSSN.unwrap({ fake: true } as never);  // throws

Codec Pipelines

Composable transformation from raw input to validated domain objects:

import { createCodec } from '@digitaldefiance/branded-interface';

const Vitals = createBrandedInterface('Vitals', {
  heartRate:   { type: 'number' },
  systolic:    { type: 'number' },
  diastolic:   { type: 'number' },
});

const vitalsCodec = createCodec(Vitals)
  .pipe((branded) => ({
    ...branded,
    bloodPressure: `${branded.systolic}/${branded.diastolic}`,
  }));

const result = vitalsCodec.execute({ heartRate: 72, systolic: 120, diastolic: 80 });
if (result.success) {
  console.log(result.value.bloodPressure); // '120/80'
}

Serialization

JSON round-trip with validation:

import { interfaceSerializer } from '@digitaldefiance/branded-interface';

const serializer = interfaceSerializer(User);

const json = serializer.serialize(user);           // '{"name":"Alice",...}'
const result = serializer.deserialize(json);       // { success: true, value: BrandedInstance }
const instance = serializer.deserializeOrThrow(json); // throws on invalid

Versioning and Migration

Schema evolution with registered migration functions:

import { addMigration, migrate } from '@digitaldefiance/branded-interface';

const AllergyV1 = createBrandedInterface('Allergy', {
  patientId: { type: 'string' },
  allergies: { type: 'string' },  // comma-separated
}, { version: 1 });

// Register migration: v1 → v2 splits string into array
addMigration(AllergyV1, 1, 2, (data) => ({
  patientId: data.patientId,
  allergies: (data.allergies as string).split(',').map((s) => s.trim()),
}));

const v1 = AllergyV1.create({ patientId: 'P001', allergies: 'Penicillin, Latex' });
const v2 = migrate(v1, 2);
// v2.allergies is now ['Penicillin', 'Latex']

Watchers

Observe create and validate events for auditing:

import { watchInterface } from '@digitaldefiance/branded-interface';

const { unwatch } = watchInterface(User, (event) => {
  console.log(`${event.eventType} on ${event.interfaceId} at ${event.timestamp}`);
});

User.create({ name: 'Bob', email: '[email protected]', age: 25 });
// Logs: "create on User at 1234567890"

unwatch(); // stop watching

Schema Diff and Intersect

Compare and analyze interface schemas:

import { interfaceDiff, interfaceIntersect } from '@digitaldefiance/branded-interface';

const diff = interfaceDiff(InterfaceA, InterfaceB);
// diff.onlyInFirst:  fields only in A
// diff.onlyInSecond: fields only in B
// diff.inBoth:       fields in both (with both descriptors)

const { definition, conflicts } = interfaceIntersect(InterfaceA, InterfaceB, 'Shared');
// definition: new interface with compatible shared fields
// conflicts:  fields with incompatible types

Structural Subtyping

Check if one interface is a structural subtype of another:

import { isSubtype } from '@digitaldefiance/branded-interface';

isSubtype(ExtendedUser, BaseUser); // true if ExtendedUser has all BaseUser fields

Decorators

TC39 stage 3 decorators for runtime validation on class properties:

import { BrandedField, BrandedClass, getBrandedConsumers, getConsumedDefinitions } from '@digitaldefiance/branded-interface';

@BrandedClass(User)
class UserService {
  @BrandedField(User)
  accessor currentUser: unknown;

  @BrandedField(Email, { optional: true })
  accessor backupEmail: string | undefined;
}

getBrandedConsumers('User');           // ['UserService']
getConsumedDefinitions('UserService'); // ['User']

JSON Schema Generation

import { interfaceToJsonSchema } from '@digitaldefiance/branded-interface';

const schema = interfaceToJsonSchema(User);
// { $schema: '...', type: 'object', title: 'User', properties: {...}, required: [...] }

// With draft version option
const schema07 = interfaceToJsonSchema(User, { draft: '07' });

Zod Schema Generation

import { interfaceToZodSchema } from '@digitaldefiance/branded-interface';

const def = interfaceToZodSchema(User);
// { interfaceId: 'User', fields: { name: { zodType: 'z.string()', optional: false, nullable: false }, ... } }

Built-in Refinement Types

Common validation patterns as pre-built branded primitives:

import { Email, NonEmptyString, PositiveInt, NonNegativeInt, Url, Uuid } from '@digitaldefiance/branded-interface';

Email.validate('[email protected]');       // true
NonEmptyString.validate('');              // false
PositiveInt.validate(42);                 // true
Uuid.validate('550e8400-e29b-41d4-a716-446655440000'); // true

API Reference

Factory

createBrandedInterface(interfaceId, schema, options?)

Creates a branded interface definition with runtime metadata and validation.

function createBrandedInterface<T extends Record<string, unknown>>(
  interfaceId: string,
  schema: InterfaceSchema,
  options?: { version?: number }
): BrandedInterfaceDefinition<T>
  • interfaceId: Unique identifier for this interface
  • schema: Object mapping field names to FieldDescriptor objects
  • options.version: Version number (default: 1)
  • Returns: Frozen definition with create(), validate(), id, schema, version
  • Idempotent: returns existing definition if ID already registered

createBrandedPrimitive(primitiveId, baseType, validateFn?)

Creates a branded primitive definition with optional validation.

function createBrandedPrimitive<T extends string | number | boolean>(
  primitiveId: string,
  baseType: 'string' | 'number' | 'boolean',
  validateFn?: (value: T) => boolean
): BrandedPrimitiveDefinition<T>
  • primitiveId: Unique identifier for this primitive
  • baseType: The underlying JavaScript type
  • validateFn: Optional predicate for additional constraints
  • Returns: Frozen definition with create(), validate(), id, baseType

createBrandedEnum(enumId, values)

Creates a branded enum for use as field refs in interface schemas.

function createBrandedEnum<T extends Record<string, string>>(
  enumId: string,
  values: T
): BrandedEnum<T>

Type Guards

isOfInterface(value, definition)

Checks if a value is a branded instance of the given interface.

function isOfInterface<T extends Record<string, unknown>>(
  value: unknown,
  definition: BrandedInterfaceDefinition<T>
): value is BrandedInstance<T>

assertOfInterface(value, definition)

Asserts a value is a branded instance, throwing if not.

function assertOfInterface<T extends Record<string, unknown>>(
  value: unknown,
  definition: BrandedInterfaceDefinition<T>
): BrandedInstance<T>

safeParseInterface(value, definition)

Safely parses a value with detailed error reporting.

function safeParseInterface<T extends Record<string, unknown>>(
  value: unknown,
  definition: BrandedInterfaceDefinition<T>
): InterfaceSafeParseResult<BrandedInstance<T>>

isOfPrimitive(value, definition)

Checks if a value is valid for a branded primitive.

function isOfPrimitive<T extends string | number | boolean>(
  value: unknown,
  definition: BrandedPrimitiveDefinition<T>
): value is T

Metadata Accessors

getInterfaceId(value)

Gets the interface ID from a branded instance or definition.

getInterfaceSchema(definition)

Gets the field schema from a branded interface definition.

getInterfaceFields(definition)

Gets the list of field names from a definition.

interfaceFieldCount(definition)

Gets the number of fields in a definition.

Registry

getAllInterfaceIds()

Returns all registered interface, primitive, and opaque type IDs.

getInterfaceById(id)

Gets a registry entry by ID. Returns { id, kind, definition } or undefined.

resetInterfaceRegistry()

Clears the global interface registry. For testing only.

Composition

| Function | Description | |---|---| | composeInterfaces(newId, ...defs) | Merge multiple interfaces (throws on duplicate fields) | | extendInterface(base, newId, fields) | Extend a base interface with additional fields | | partialInterface(def, newId) | Make all fields optional | | pickFields(def, newId, fields) | Keep only specified fields | | omitFields(def, newId, fields) | Remove specified fields |

Analysis

| Function | Description | |---|---| | interfaceDiff(first, second) | Partition fields into onlyInFirst, onlyInSecond, inBoth | | interfaceIntersect(first, second, newId) | Create interface from compatible shared fields | | isSubtype(candidate, supertype) | Check structural subtype relationship |

Schema Generation

| Function | Description | |---|---| | interfaceToJsonSchema(def, options?) | Generate JSON Schema (draft 2020-12 or 07) | | interfaceToZodSchema(def) | Generate Zod-compatible schema definition |

Other

| Function | Description | |---|---| | createBuilder(id) | Fluent builder for interface definitions | | createOpaqueType(typeId, baseType) | Opaque type with wrap() / unwrap() | | createCodec(def) | Codec pipeline with .pipe() and .execute() | | interfaceSerializer(def) | JSON serializer with serialize() / deserialize() / deserializeOrThrow() | | addMigration(def, from, to, fn) | Register a version migration | | migrate(instance, targetVersion) | Apply migrations to reach target version | | watchInterface(def, callback) | Watch create/validate events, returns { unwatch } | | BrandedField(def, options?) | TC39 accessor decorator for property validation | | BrandedClass(...defs) | Class decorator for usage tracking |

Types

// Field descriptor for interface schemas
interface FieldDescriptor {
  type: 'string' | 'number' | 'boolean' | 'object' | 'array'
      | 'branded-enum' | 'branded-interface' | 'branded-primitive';
  optional?: boolean;
  nullable?: boolean;
  validate?: (value: unknown) => boolean;
  ref?: string;       // reference to a registered branded type ID
  items?: FieldDescriptor; // for array element types
}

// A branded instance — frozen data + Symbol metadata
type BrandedInstance<T> = Readonly<T> & BrandedInterfaceMetadata;

// Interface definition returned by createBrandedInterface()
interface BrandedInterfaceDefinition<T> {
  id: string;
  schema: InterfaceSchema;
  version: number;
  create: (data: T) => BrandedInstance<T>;
  validate: (data: unknown) => data is T;
}

// Primitive definition returned by createBrandedPrimitive()
interface BrandedPrimitiveDefinition<T> {
  id: string;
  baseType: 'string' | 'number' | 'boolean';
  create: (value: T) => T & { readonly __brand: string };
  validate: (value: unknown) => value is T;
}

// Opaque type definition returned by createOpaqueType()
interface OpaqueTypeDefinition<T> {
  id: string;
  wrap: (value: T) => OpaqueValue<T>;
  unwrap: (opaque: OpaqueValue<T>) => T;
}

Cross-Bundle Registry

The global registry uses globalThis, ensuring all branded types are tracked across different bundles, ESM/CJS modules, and even different instances of the library. Interfaces, primitives, and opaque types share a single registry with kind-based collision detection.

License

MIT © Digital Defiance