A resolver that throws on a 403 or 404 forces every consuming component into a generic router error page, discarding the specific reason the navigation should be handled differently. Returning a discriminated union instead lets `ActivatedRoute.data` carry a typed `kind` field the component template can `@switch` on, so a paywall message and a not-found message stay distinct without a second round-trip. This trades the router's built-in error-handling pipeline for explicit, exhaustively-checked branching in the component — the resolver never rejects, so `withComponentInputBinding` or a route guard can't accidentally swallow the failure reason.
import { ResolveFn } from '@angular/router';import { inject } from '@angular/core';type LoadResult =| { kind: 'ok'; payload: { id: string; title: string } }| { kind: 'forbidden'; reason: string }| { kind: 'missing' };export const articleResolver: ResolveFn<LoadResult> = async (route) => {const id = route.paramMap.get('id');if (!id) {return { kind: 'missing' };}const service = inject(ArticleGateway);const res = await service.fetchRaw(id);if (res.status === 403) {return { kind: 'forbidden', reason: 'not-subscribed' };}if (res.status === 404) {return { kind: 'missing' };}return { kind: 'ok', payload: res.body };};declare class ArticleGateway {fetchRaw(id: string): Promise<{ status: number; body: { id: string; title: string } }>;}