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
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); // 32Use 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 Parameter | Default type | Description |
|---|---|---|
Edges extends Edge[] | [] | Tuple of conversion edges accumulated so far |
Methods
allow()
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
| Parameter | Type | Description |
|---|---|---|
from | FromMeta | UnitsFor<From> | Source unit (string name or metadata object) |
to | ToMeta | 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
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()
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
| Parameter | Type | Description |
|---|---|---|
value | From | Value to convert |
fromUnit | UnitsFor<From> | Source unit |
Returns
{
to: To;
}Object with to() method for conversion
| Name | Type | Defined in |
|---|---|---|
to() | (unit) => To | packages/core/src/registry.ts:300 |
getConverter()
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
| Parameter | Type | Description |
|---|---|---|
from | UnitsFor<From> | Source unit |
to | UnitsFor<To> | Destination unit |
Returns
Converter<From, To> | undefined
Converter function, or undefined if no path exists
register()
Call Signature
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
| Parameter | Type |
|---|---|
unit | FromMeta |
Returns
UnitRegistry<Edges> & { [K in string]: UnitAccessor<From, Edges> }
Call Signature
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
| Parameter | Type | Description |
|---|---|---|
from | FromMeta | NameFor<From> | Source unit (string name or metadata object) |
to | ToMeta | NameFor<To> | Destination unit (string name or metadata object) |
converter | RelaxedConverter<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
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
| Parameter | Type | Description |
|---|---|---|
from | FromMeta | NameFor<From> | First unit (string name or metadata object) |
to | ToMeta | NameFor<To> | Second unit (string name or metadata object) |
converter | RelaxedBidirectionalConverter<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