Skip to content

fix(router): expose a public RouteResource type for route resources - #71300

Open
lacolaco wants to merge 2 commits into
angular:mainfrom
lacolaco:router-resource-type
Open

lacolaco wants to merge 2 commits into
angular:mainfrom
lacolaco:router-resource-type

Conversation

@lacolaco

Copy link
Copy Markdown
Contributor

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.dev application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

A nonBlocking() route resource is bound to a component input as the router-wrapped resource, which supports reload() but not set() or update(). There is no public type for it: Resource<T> has no reload(), and WritableResource<T> / ResourceRef<T> declare methods the router-wrapped resource does not implement. Users have to declare their own Resource<T> & {reload(): boolean} to type the input. #70702 gave ActivatedRoute.resources a reload() through an anonymous intersection in ResourceResult, which cannot be reused for component inputs.

Issue Number: #71299

What is the new behavior?

RouteResource<T> is exported from @angular/router and used as the value type of ResourceResult, so a non-blocking resource input can be declared as input.required<RouteResource<T>>() and reload() can be called on it without a cast. The data fetching guide uses this type in the non-blocking example.

The name follows the "route resources" terminology of the routing guide and Route.resources.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

This keeps the type in @angular/router. A framework-wide ReloadableResource<T> in @angular/core, mentioned in the #70702 review, is left out of this PR.

Non-blocking route resources are bound to component inputs as the
router-wrapped resource, which supports `reload()` but not `set()` or
`update()`. There was no public named type for it, so users had to
declare their own intersection type to receive it as an input.

Add `RouteResource<T>` and use it for `ResourceResult` and the return
type of the internal `routerResource()`.

Fixes angular#71299
Update the route resources guide to type non-blocking resource inputs
with `RouteResource` and mention the type exposed by the router.
@pullapprove
pullapprove Bot requested a review from atscott October 11, 2026 12:36
@angular-robot angular-robot Bot added area: docs Related to the documentation area: router labels Oct 11, 2026
@ngbot ngbot Bot added this to the Backlog milestone Oct 11, 2026
@atscott

atscott commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

I don鈥檛 feel like Router should own a type for this, which is why one wasn't exposed here.

@lacolaco

lacolaco commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor Author

@atscott Thanks for the context. I understand the hesitation about the router owning this type, and I'm open to where it should live.

From actually using route resources, though, I've found a public type for this model to be necessary in practice, and not only for typing input<T>(). ActivatedRoute.resources is typed as ResourceResult, so every value is a Resource<unknown> with reload(). Since there is no generic type to use, avoiding unknown while keeping reload() means writing the same type with a concrete T:

private readonly users = inject(ActivatedRoute).resources?.['users'] as
  (Resource<User[]> & { reload(): boolean }) | undefined;

So whether the resource reaches the component through an input or through ActivatedRoute, users need the same generic type, and today every app has to define it on its own.

Since this isn't really router-specific, I agree it would fit better in @angular/core. Would an intermediate ReloadableResource<T> there (as suggested in the #70702 review) be acceptable until reload can move to the base Resource interface? If so, I'm happy to rework this PR to add it to core and have the router's ResourceResult reference it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: docs Related to the documentation area: router

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants