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

angular-typed-router

v0.1.1-4

Published

## Attention: Still in experimental phase

Downloads

14

Readme

angular-typed-router

Attention: Still in experimental phase

Type-safe ergonomic primitives on top of Angular's standalone router. Automatically derive a compile‑time union of valid route URL strings (Path) and strongly typed navigation command tuples (Commands) from the consumer application's Routes definition – without generating code or adding runtime weight.

Why

Angular's router is powerful but untyped for URL literals – a misspelled path or an outdated segment only fails at runtime. This library lets your application declare routes once, then:

  • Navigate with TypedRouter.navigateByUrl(path) where path is validated at compile time.
  • Use <a routerLink="..."> with type checking via an augmented TypedRouterLink directive.
  • Build command tuples with correct literal segments (Commands type).

No decorators, no custom builders, no code generation – just TypeScript type inference and interface augmentation.

Installation

ng add angular-ryped-router will set up the package and create a declaration file for you.

Or install manually:

npm install angular-typed-router
# or
pnpm add angular-typed-router
# or
yarn add angular-typed-router

Quick Start

  1. Define your application routes:
// app.routes.ts
import { Routes } from '@angular/router';
import { DashboardComponent } from './dashboard.component';

export const appRoutes = [
  { path: 'dashboard', component: DashboardComponent },
  { path: 'projects/:id', loadComponent: () => import('./project.component').then((m) => m.ProjectComponent) },
  { path: '**', redirectTo: 'dashboard' },
] as const satisfies Routes;
  1. Create the augmentation file so the library can “see” your routes:
// typed-router.d.ts (sibling to main.ts or inside src/ root)
import type { appRoutes } from './app/app.routes';

declare module 'angular-typed-router' {
  interface UserTypedRoutes {
    routes: typeof appRoutes;
  }
  // Customize route param types here, the keys of this interface match your route param names
  interface RouteParamTypes {
    id: `${number}`;
    // other params...
  }
}
  1. Link the augmentation file in your tsconfig so the compiler includes it:
// tsconfig.app.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {},
  "include": [
    "src/**/*.ts",
    "typed-router.d.ts" // <— add this line
  ]
}

If you have multiple tsconfigs, ensure the specific app tsconfig that drives the build/test includes the file.

  1. Use the typed router & link:
import { Component, inject } from '@angular/core';
import { TypedRouter, Path } from 'angular-typed-router';

@Component({
  selector: 'app-nav',
  template: `
    <a routerLink="dashboard">Dashboard</a>
    <a routerLink="projects/123">Project 123</a>
  `,
})
export class NavComponent {
  private readonly router = inject(TypedRouter);

  go(p: Path) {
    // p must be one of the inferred paths
    this.router.navigateByUrl(p);
  }

  openProject(id: string) {
    // commands tuple – first literal segment + dynamic param value
    this.router.navigate(['projects', id]);
  }
}

If you try this.router.navigateByUrl('projcts/123') (typo) or <a routerLink="projcts/123">, TypeScript errors.

Exports

import { TypedRouter, TypedRouterLink, Path, Commands, UserTypedRoutes, RouteParamTypes } from 'angular-typed-router';
  • TypedRouter – Extends Angular Router, overrides navigateByUrl & navigate signatures to accept Path / Commands.
  • TypedRouterLink – Directive shadowing [routerLink] to type its input as Commands | Path.
  • Path – Union of every reachable concrete URL path produced from your route tree (includes parameterized expansions with string in place of :param segments).
  • Commands – Union of tuple command arrays representing valid Router.navigate() inputs (each static segment as a literal, each parameter position as string).
  • UserTypedRoutes – Empty interface you augment with your routes reference.
  • ExtractPathsFromRoutes<Routes> – Utility type if you need to compute from an arbitrary Routes array manually.
  • RouteParamTypes – Interface you can augment to specify types for route parameters by name (e.g. id: ${number}).

How It Works

  1. You augment UserTypedRoutes with the literal const route array.
  2. Type utilities recursively walk the route tree (including lazily loaded routes via loadChildren returning Route[] or { routes }).
  3. Each navigable route (component / loadComponent / redirectTo) contributes a path string. Param segments (e.g. :id) can be typed through declaration merging of RouteParamTypes.
  4. Child paths are joined with parents to form final concrete path unions.
  5. A tuple transformation creates the Commands variants.

All compile-time only; nothing ships to runtime.

Lazy Routes

Works with any lazy route whose loadChildren resolves to:

  • Promise<Route[]>
  • Promise<{ routes: Route[] }> (Angular v17+ pattern)

Example:

{ path: 'admin', loadChildren: () => import('./admin.routes').then(m => m.adminRoutes) }

Those child paths get prefixed (admin/...) in Path & Commands.

Parameter Segments

A pattern projects/:id/details/:section produces a Path variant like:

'projects/' + IdParamType + '/details/' + SectionParamType

and a Commands tuple like:

['projects', IdParamType, 'details', SectionParamType]

You pass real runtime values for the IdParamType and SectionParamType positions. Empty string values and values like 'param/still-param' cannot currently be prevented at the type level without hurting DX (see Limitations).

Usage Patterns

Navigate by full path (typed):

router.navigateByUrl('/dashboard');

Navigate with commands array:

router.navigate(['/', 'projects', someId]);

Generate a UrlTree:

router.createUrlTree(['/', 'projects', id]);

Template links:

<a routerLink="/projects/42">Project 42</a> <a [routerLink]="['/', 'projects', projectId]"></a>

Augmentation Placement

Keep the augmentation in a .d.ts that is included by tsconfig.app.json (include array). If you see Path still as never, ensure:

  • The augmentation file is included.
  • The routes constant is as const satisfies Routes.
  • No circular import (augmentation file should only import the routes, nothing else runtime-heavy).

Limitations & Tradeoffs

| Concern | Status / Rationale | | ---------------------------------- | ------------------------------------------------------------------------------------- | | relativeTo (relative navigation) | Not supported – all inferred Path / Commands are absolute. Use absolute commands. |

ESLint Recommendation (Optional)

You can use angular-typed-router-eslint plugin to forbid untyped navigation calls.

Troubleshooting

| Symptom | Fix | | --------------------- | -------------------------------------------------------------- | | Path is never | Check augmentation file is included in tsconfig. | | Lazy children missing | Ensure promise resolves to Route[] or { routes: Route[] }. |

Contributing

PRs welcome.

License

MIT


Happy routing.