Angular TypeScript Agent Rules
Project Context
- Use Angular 17+ with standalone components as the default — no NgModules unless integrating legacy libraries.
- Enable `strict` in `tsconfig.json` with `strictNullChecks`, `noImplicitAny`, and `noImplicitOverride`.
- Use the Angular CLI for all scaffolding: `ng generate component`, `ng generate service`, `ng generate guard`.
- Configure `provideRouter` in `app.config.ts` with `withComponentInputBinding()` to bind route params to `input()` signals.
Code Style & Structure
- Prefer `inject()` over constructor injection for all services in components, directives, and pipes.
- Use `input()`, `output()`, and `model()` signal-based primitives instead of `@Input()`, `@Output()` decorators.
- Name files with Angular suffix convention: `user-profile.component.ts`, `auth.service.ts`, `admin.guard.ts`.
- Keep component files under 150 lines. Extract logic into services or composable functions.
- Use `readonly` for injected services: `private readonly userService = inject(UserService)`.
- Place business logic in services, not components. Components are responsible only for presentation.
- Follow feature-folder structure: each feature contains its component, service, types, and routes together.
Signals & Reactivity
- Use `signal()` for local mutable state. Use `computed()` for derived values — never recompute in templates.
- Use `effect()` only for side effects that respond to signal changes, such as syncing to localStorage.
- Avoid calling `effect()` inside conditionals or loops — only at component construction time.
- Replace `AsyncPipe` with `toSignal()` to unwrap observables into signals without subscription boilerplate.
- Use `toObservable()` when passing signal values to RxJS operators in service layers.
- Use `linkedSignal()` (Angular 19+) when a signal must reset when another signal changes.
Component Patterns
- Set `changeDetection: ChangeDetectionStrategy.OnPush` on every component — signals trigger updates automatically.
- Use `@defer` blocks to lazy-load heavy components with `@loading`, `@error`, and `@placeholder` fallbacks.
- Use `@if`, `@for`, and `@switch` control flow (Angular 17+) instead of `*ngIf`, `*ngFor`, `*ngSwitch`.
- Add `track` expressions to `@for` loops: `@for (item of items(); track item.id)`.
- Use `hostDirectives` to compose reusable behaviors onto components without class inheritance.
- Avoid `ngOnChanges`. Use `computed()` or `effect()` reacting to `input()` signals instead.
Services & Dependency Injection
- Use `providedIn: 'root'` for singleton services. Scope feature services in the route's `providers` array.
- Use `HttpClient` with typed generics: `this.http.get<User[]>('/api/users')`.
- Intercept requests with functional interceptors (`HttpInterceptorFn`) rather than class-based ones.
- Use `takeUntilDestroyed()` from `@angular/core/rxjs-interop` to auto-unsubscribe in components.
- Use `InjectionToken<T>` for configuration values and non-class dependencies.
Routing
- Use `loadComponent` for lazy-loaded standalone routes.
- Use functional route guards returning `CanActivateFn` — no class-based guards.
- Use `withPreloading(PreloadAllModules)` or a custom preloading strategy for performance-critical routes.
- Bind route params to component inputs via `withComponentInputBinding()` — no need to inject `ActivatedRoute`.
Error Handling
- Use `ErrorHandler` for global uncaught error reporting. Integrate monitoring tools inside the custom handler.
- Handle HTTP errors in services with `catchError` — return typed error objects, never rethrow `any`.
- Use `HttpErrorResponse` for type-safe error handling in interceptors.
- Throw `redirect()` or `Response` objects from resolvers and guards for expected failures (401, 404).
Performance
- Use `@defer (on viewport)` to defer rendering of below-the-fold components until visible.
- Avoid function calls in templates — use `computed()` signals or pure `pipe` transforms instead.
- Use `NgOptimizedImage` (`<img ngSrc>`) for automatic LCP optimization, lazy loading, and sizing hints.
- Enable `withIncrementalHydration()` in SSR builds to hydrate only interactive components.
Testing
- Test components with `TestBed.configureTestingModule` and `ComponentFixture`.
- Use `TestBed.overrideProvider` to inject mock services; avoid `spyOn` on private members.
- Test signal state by calling `componentRef.setInput()` and asserting `fixture.componentInstance.someSignal()`.
- Write unit tests for services in isolation using `TestBed.inject()` with `provideHttpClientTesting()`.
- Use `fakeAsync` and `tick()` for timer-based behavior; use `firstValueFrom` to await observables in tests.