Code Quality Help

TypeScript and Strict Typing Requirements

Compiler Configuration

Every library must extend a shared base configuration that enforces strict type‑checking. The library‑level tsconfig.lib.json must enable at least the following compiler options, either directly or via the base config:

{ "extends": "../../tsconfig.base.json", "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, "noUnusedParameters": true } }

The strict flag must remain true; disabling individual strict‑mode sub‑options is not allowed. Any deviation (e.g., temporarily relaxing a rule during an emergency fix) requires an explicit comment in the pull request and must be tracked for removal.

General Coding Standards

  • Leverage readonly for properties that are not meant to be mutated after construction, especially in public API models and configuration objects.

  • Use as const for literal values and discriminated unions where the exact shape should be preserved.

  • Type guards and assertion functions are encouraged to encapsulate runtime checks and provide compile‑time safety.

Component Typing and Signals

Angular components must be strictly typed, with a strong preference for the signal‑based API introduced in Angular 16+ for inputs, outputs, and model bindings. Signals improve change detection, enable better type inference, and align with the framework’s future direction.

Required: input() and output() signals

  • Input properties must be declared using the input() function (from @angular/core), providing a default value and an explicit type:

readonly filterKey = input<string>(''); readonly items = input<FilterItem[]>([], { transform: validateItems });
  • Output properties must be declared using the output() function:

    readonly filterChanged = output<FilterChangeEvent>();
  • Two‑way binding uses the model() function:

    readonly searchTerm = model<string>('');

Exceptions to the Signal Requirement

The traditional @Input() and @Output() decorators are allowed only when:

  • The library must support Angular versions older than 17 where the signal API is unavailable (only if the library explicitly targets those versions).

  • Interaction with a third‑party library that depends on class property decorators or set/get pairs cannot be migrated.

  • The component is part of a legacy code path that has a concrete plan for migration and the exception has been approved by the team lead.

Any such exception must be documented in the pull request description with a clear technical justification. A comment must also appear in the source code above the decorated property, referencing the issue or reason.

Component and Directive Typing Rules

  • All inputs, outputs, and query results (viewChild, contentChild) must be explicitly typed. Use generic type parameters:

    readonly filterInputEl = viewChild<ElementRef<HTMLInputElement>>('filterInput'); readonly chipComponents = contentChildren<FilterChipComponent>(FilterChipComponent);
  • The @Component and @Directive decorators must include standalone: true for all new components and directives. Libraries should avoid adding to an NgModule unless backward compatibility requires it.

  • Public methods exposed on components for template or parent interaction must have explicit return types and parameter types; avoid relying on inference for the public API surface.

Service and Utility Typing

  • Services, facades, and utilities must export typed interfaces for their configuration and result shapes. Use interface over type for public contracts unless union or intersection traits make type more appropriate.

  • Dependency injection tokens should be typed using InjectionToken<T> with a clear T:

    export const FILTER_CONFIG = new InjectionToken<FilterConfig>('FILTER_CONFIG');
  • Return types of all public service methods must be explicitly annotated. Generic methods should be constrained appropriately (<T extends SomeType>).

Model and Data Shape Definitions

  • All data shapes shared between components or exposed from public-api.ts must be defined as standalone interfaces, enums, or readonly types in dedicated *.models.ts or *.types.ts files.

  • Prefer interface for objects; use enum only when a set of known constants with reverse mapping is needed (numeric enum) or string union with as const when the mapping is not required.

  • Complex union types should be expressed as discriminated unions with a literal kind or type property, enabling exhaustive checking in switch statements.

Linting and CI Enforcement

  • The ESLint configuration must include @typescript-eslint/no-explicit-any set to error, along with rules that prevent unsafe casting (@typescript-eslint/consistent-type-assertions).

  • A separate rule must warn when traditional @Input()/@Output() decorators are used without an accompanying justification comment; this can be implemented via a custom ESLint rule or at minimum a code‑review checklist.

  • The CI pipeline runs the TypeScript compiler with --noEmit (or ng lint including type‑check) and must block any merge when strict mode errors are present.

All source code must compile without errors or warnings under the strictest settings. Suppressing TypeScript errors with // @ts-ignore or // @ts-expect-error is acceptable only when accompanied by a comment explaining the necessity and only in the narrow scope described in 3.2.

03 July 2026