Skip to content

unacy / UnitRegistry

Interface: UnitRegistry<Edges>

Defined in: packages/core/src/registry.ts:200

Registry for managing and composing unit converters.

The central API of unacy. Each register() call returns a new registry instance (immutable accumulator pattern) whose static type reflects the newly added edge(s). At runtime, the registry stores edges in an adjacency map and runs BFS when a direct edge is absent.

Remarks

Immutability: every register() and allow() call produces a new registry. Assigning the result back to the same variable is intentional — the old instance remains valid but lacks the new edge in its type.

BFS caching: multi-hop paths resolved via getConverter are cached by a "from->to" key. Mutating the graph after caching is not supported — the registry is designed to be built once and used many times.

Example

typescript
const registry = createRegistry()
  .register(CelsiusMeta, FahrenheitMeta, { to: c => (c * 9/5) + 32, from: f => (f - 32) * 5/9 })
  .register(CelsiusMeta, KelvinMeta, c => c + 273.15)
  .allow(KelvinMeta, FahrenheitMeta);

registry.Celsius.to.Fahrenheit(0 as Celsius); // 32
registry.Kelvin.to.Fahrenheit(273.15 as Kelvin); // 32

Use When

You need a central, type-safe registry for a domain's units (e.g., temperature, length, currency) that enforces conversion safety at compile time.

Avoid When

You only need a single, always-direct conversion without BFS composition — a plain Converter<A, B> function is simpler.

Pitfalls

NEVER mutate the internal graph after calling getConverter on a path that was composed via BFS — the path cache is not invalidated on mutation and will return stale composed converters.

NEVER discard the return value of register() — the original registry instance does not have the new edge in its type or runtime graph.

NEVER share a mutable registry reference across async boundaries where concurrent calls could interleave register and convert — the registry is designed to be fully built before any conversions are performed.

See

  • createRegistry
  • UnitMap
  • UnitAccessor

Type Parameters

Type ParameterDefault typeDescription
Edges extends Edge[][]Tuple of conversion edges accumulated so far

Methods

allow()

ts
allow<From, To, FromMeta, ToMeta>(from, to): UnitRegistry<[...Edges[], Edge<From, To>]> & UnitMap<[...Edges[], Edge<From, To>]>;

Defined in: packages/core/src/registry.ts:266

Explicitly allow a conversion path in the type system (for multi-hop conversions)

This method verifies that a conversion path exists at runtime (via BFS) and adds it to the type system so it can be used with type-safe accessor syntax.

Type Parameters

Type Parameter
From extends any
To extends any
FromMeta extends { name: string; type: | ClassType | RecordSchema | keyof PrimitiveTypeMap | EnumType | TupleSchema; }
ToMeta extends { name: string; type: | ClassType | RecordSchema | keyof PrimitiveTypeMap | EnumType | TupleSchema; }

Parameters

ParameterTypeDescription
fromFromMeta | UnitsFor<From>Source unit (string name or metadata object)
toToMeta | UnitsFor<To>Destination unit (string name or metadata object)

Returns

UnitRegistry<[...Edges[], Edge<From, To>]> & UnitMap<[...Edges[], Edge<From, To>]>

New registry instance with the conversion path enabled in types

Throws

ConversionError if no path exists between the units

Example

typescript
const registry = createRegistry()
  .register('Celsius', 'Kelvin', c => (c + 273.15) as Kelvin)
  .register('Kelvin', 'Fahrenheit', k => ((k - 273.15) * 9/5 + 32) as Fahrenheit)
  .allow('Celsius', 'Fahrenheit'); // Enable multi-hop path in types

// Now type-safe:
const f = registry.Celsius.to.Fahrenheit(temp);

convert()

ts
convert<From>(value, fromUnit): {
  to: To;
};

Defined in: packages/core/src/registry.ts:296

Convert a value using fluent API

Type Parameters

Type Parameter
From extends WithUnits<SupportedType, BaseMetadata>

Parameters

ParameterTypeDescription
valueFromValue to convert
fromUnitUnitsFor<From>Source unit

Returns

ts
{
  to: To;
}

Object with to() method for conversion

NameTypeDefined in
to()(unit) => Topackages/core/src/registry.ts:300

getConverter()

ts
getConverter<From, To>(from, to): Converter<From, To> | undefined;

Defined in: packages/core/src/registry.ts:282

Get a converter (direct or composed via BFS)

Type Parameters

Type Parameter
From extends WithUnits<SupportedType, BaseMetadata>
To extends WithUnits<SupportedType, BaseMetadata>

Parameters

ParameterTypeDescription
fromUnitsFor<From>Source unit
toUnitsFor<To>Destination unit

Returns

Converter<From, To> | undefined

Converter function, or undefined if no path exists


register()

Call Signature

ts
register<From, FromMeta>(unit): UnitRegistry<Edges> & { [K in string]: UnitAccessor<From, Edges> };

Defined in: packages/core/src/registry.ts:201

Type Parameters
Type Parameter
From extends any
FromMeta extends { name: string; type: | ClassType | RecordSchema | keyof PrimitiveTypeMap | EnumType | TupleSchema; }
Parameters
ParameterType
unitFromMeta
Returns

UnitRegistry<Edges> & { [K in string]: UnitAccessor<From, Edges> }

Call Signature

ts
register<From, To, FromMeta, ToMeta>(
   from, 
   to, 
converter): UnitRegistry<[...Edges[], Edge<From, To>]> & UnitMap<[...Edges[], Edge<From, To>]>;

Defined in: packages/core/src/registry.ts:212

Register a unidirectional converter

Type Parameters
Type Parameter
From extends any
To extends any
FromMeta extends { name: string; type: | ClassType | RecordSchema | keyof PrimitiveTypeMap | EnumType | TupleSchema; }
ToMeta extends { name: string; type: | ClassType | RecordSchema | keyof PrimitiveTypeMap | EnumType | TupleSchema; }
Parameters
ParameterTypeDescription
fromFromMeta | NameFor<From>Source unit (string name or metadata object)
toToMeta | NameFor<To>Destination unit (string name or metadata object)
converterRelaxedConverter<From, To>Converter function (input is branded, output can be plain or branded)
Returns

UnitRegistry<[...Edges[], Edge<From, To>]> & UnitMap<[...Edges[], Edge<From, To>]>

New registry instance with the converter registered

Call Signature

ts
register<From, To, FromMeta, ToMeta>(
   from, 
   to, 
converter): UnitRegistry<[...Edges[], Edge<From, To>, Edge<To, From>]> & UnitMap<[...Edges[], Edge<From, To>, Edge<To, From>]>;

Defined in: packages/core/src/registry.ts:230

Register a bidirectional converter (both directions)

Type Parameters
Type Parameter
From extends any
To extends any
FromMeta extends { name: string; type: | ClassType | RecordSchema | keyof PrimitiveTypeMap | EnumType | TupleSchema; }
ToMeta extends { name: string; type: | ClassType | RecordSchema | keyof PrimitiveTypeMap | EnumType | TupleSchema; }
Parameters
ParameterTypeDescription
fromFromMeta | NameFor<From>First unit (string name or metadata object)
toToMeta | NameFor<To>Second unit (string name or metadata object)
converterRelaxedBidirectionalConverter<From, To>Bidirectional converter object (input branded, output can be plain or branded)
Returns

UnitRegistry<[...Edges[], Edge<From, To>, Edge<To, From>]> & UnitMap<[...Edges[], Edge<From, To>, Edge<To, From>]>

New registry instance with both converters registered

Released under the MIT License.