Angular / TypeScript Standards
This section covers language-specific and framework-specific practices for Angular applications written in TypeScript. They complement the general naming and formatting rules from Sections 3 and 4.
6.1 TypeScript Strictness
Strict mode: Every TypeScript project must use
"strict": trueintsconfig.json. This enables all strict type-checking options (noImplicitAny,strictNullChecks, etc.).Explicit typing: Avoid
anyunless absolutely necessary (e.g., third-party libraries with incomplete types). Whenanyis used, add a// TODOcomment with the Jira ticket for adding proper types. The@typescript-eslint/no-explicit-anyrule is set to error.Type annotations: Always annotate function return types and public method signatures. Do not rely on inference for public API boundaries.
// Good getUser(id: string): Observable<User> { ... } // Bad getUser(id: string) { ... }Interfaces vs Classes: Prefer interfaces for DTOs, input/output models, and service contracts. Use classes only when logic is needed (components, services, guards).
6.2 Component Design
One component per file: Each Angular component must reside in its own file.
Component lifecycle: Implement lifecycle hooks only when needed. Use
implementskeyword for hooks to leverage IDE assistance:export class MyComponent implements OnInit, OnDestroy { ... }Change detection strategy: Prefer
ChangeDetectionStrategy.OnPushby default. Only useDefaultwhen justified and documented.Inputs and Outputs:
Use
@Input()and@Output()decorators, followed by the property.Input types should be narrow and, where possible, primitive; avoid passing complex objects for simple bindings.
Use aliases only when the external API name differs from the internal name, and keep alias naming consistent across components.
Output event emitters must end with a verb in the past tense (e.g.,
dataLoaded,selectionChanged).
View encapsulation: Default (Emulated) is recommended. If
Noneis used, document the reason and ensure styles do not leak unintentionally.Template size: If a template exceeds ~200 lines, extract child components. This improves readability and testability.
6.3 Templates
Data binding syntax: Use
[property]="..."for property binding and(event)="handler($event)"for event binding. Avoidon-prefix for events.Avoid function calls in templates: Avoid calling methods directly in data-binding expressions, especially complex ones, as they impact change detection. Use pure pipes or pre-calculated properties instead.
Async pipe: Always use the
asyncpipe in templates to subscribe to Observables. This guarantees automatic unsubscription on component destroy.Attribute directives: Prefer functional directives for small DOM manipulations. Use structural directives (
*ngIf,*ngFor) sparingly to avoid deep nesting.Local references: Use
#namefor template reference variables; prefer descriptive names (e.g.,#submitButton,#searchInput).
6.4 Observables and RxJS
Naming convention: Suffix Observable variables with
$(e.g.,users$,searchResults$).Subscription management:
In templates, use
asyncpipe – no manual subscription needed.In component classes, when you must subscribe manually, use
takeUntilwith aSubject<void>and callnext/completeinngOnDestroy. Do not leave open subscriptions.private destroy$ = new Subject<void>(); ngOnInit(): void { this.dataService.getData() .pipe(takeUntil(this.destroy$)) .subscribe(data => this.data = data); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); }Alternatively, if only one value is expected, use
first()ortake(1).
Avoid nested subscribes: Use higher-order mapping operators (
switchMap,mergeMap,concatMap,exhaustMap) instead of subscribing inside another subscription.Error handling: Always include error handling in observable chains, either with
catchErrorin the pipe, or viaerrorcallback in subscribe. Unhandled errors can crash the application.Hot vs Cold observables: Be aware of the difference and document when an observable is shared (e.g., using
shareReplay) to avoid side effects.
6.5 Services and Dependency Injection
Provided in root: Use
@Injectable({ providedIn: 'root' })for global singleton services. If a service is scoped to a specific module or component, specify the provider at that level.No logic in constructor: The constructor of a service or component must only inject dependencies. Do not perform side effects, HTTP calls, or complex initialization; use
ngOnInitor factory methods for that.Avoid service locator: Do not inject
InjectororReflectiveInjectorto manually retrieve services. Use proper DI.
6.6 Forms
Reactive forms over template-driven: Use reactive forms (
FormBuilder,FormGroup,FormControl) for all but the simplest static forms. They provide better type safety and testability.Typed forms: Utilize Angular 14+ typed reactive forms. Define interfaces matching the form structure.
interface UserForm { name: FormControl<string>; age: FormControl<number>; } form: FormGroup<UserForm> = this.fb.group({ name: ['', Validators.required], age: [0, [Validators.required, Validators.min(0)]], });Validation logic: Keep synchronous validators in separate files for reusability. Async validators should avoid making repeated HTTP requests unnecessarily.
Form layout: Prefer
formGroupandformControlNamedirectives over manual binding to FormControl instances. This keeps templates clean.
6.7 Additional Best Practices
Pipes over methods: For data transformations in templates, use Angular pipes. They are optimized for change detection. If logic is needed in TS, consider pure pipes.
Modules vs Standalone: Prefer standalone components, directives, and pipes for new code. This reduces NgModule boilerplate and improves tree-shaking.
Lazy loading: Components that are not part of the initial view should be lazy loaded (via routes) to reduce bundle size.
Avoid direct DOM manipulation: Use Angular APIs (Renderer2, ViewChild) when DOM access is required. Do not use
document.getElementByIdorelementRef.nativeElementmanipulation directly unless strictly necessary.Testing (covered in detail in Testing section): Every component and service must have at least a basic unit test with TestBed.