Skip to content

ui-router-registry ​

NPM VersionGitHub Release

ui-router-registry types the state names and params of a @uirouter/core router from the app's own as const state declarations. It is types only and emits no JavaScript.

Installation ​

bash
npm install --save-dev ui-router-registry

@uirouter/core is a peer dependency, for its types only. Install one copy of this package per app: two copies mean two Registers, and the augmentation types only one.

Registering states ​

Register the app's declarations once:

ts
import type { states } from './states';

declare module 'ui-router-registry' {
  interface Register {
    states: (typeof states)[number];
  }
}

Until an app registers, every type falls back to core's string and RawParams, so existing calls compile unchanged.

Typed calls ​

Annotate where the router is used. Core's StateService is assignable to TypedStateService, so the annotation needs no cast:

ts
import type { TypedStateService } from 'ui-router-registry';

const stateService: TypedStateService = router.stateService;

stateService.go('contacts.contact', { contactId: 'c1' });
TypeWhat it holds
Registerthe interface an app augments with its as const state declarations
StateNamethe registered state names; dotted names and parent: both resolve, and a future state's name.** placeholder is left out
ParamsOf and ResolvedParamsOfthe params a state accepts and the params it reads back, its ancestors' included; url placeholders are required, query params are not
TypedStateServicego, transitionTo, target, href, is and includes over registered names; relative refs are checked by their tail, and object targets stay loose
TypedRouteSnapshotthe route snapshot the reactive packages share, whose includes(name) narrows current and params to that state's subtree

Relative refs and unchecked targets ​

A relative ref (.child, ^, ^.sibling) resolves against the uiSref's context or options.relative, which types can't see. Its tail is checked against the registered names, so .contcat fails, and its params stay loose. A relative ref also lets a link reuse the current params instead of restating them: uiSref('.edit') from contacts.contact inherits contactId.

A target the registry can't see, such as a name built at runtime or a state registered at runtime, is a string and fails once an app registers. Opt out explicitly with UncheckedRef; its params are loose:

ts
import type { UncheckedRef } from 'ui-router-registry';

uiSref(link.state as UncheckedRef, link.params);
stateService.go(name as UncheckedRef);

Annotate members that call typed targets ​

A state declaration that names a component and is checked with satisfies against a type that inspects the component, such as LitStateDeclaration, makes that component part of Register's type. If one of the component's members calls a typed target (go, href) and has no declared return type, TypeScript has to infer that member from Register while still building Register, and reports a circular type (TS7022, TS7023, TS2456). Declare the member's return type:

ts
render(): TemplateResult {
  return html`<a href=${this.stateService.href('contacts.contact', { contactId })}>…</a>`;
}

Further reading ​