Key Features of Angular


Key Features of Angular Interview with follow-up questions

1. What are the key features of Angular?

Angular's defining features, framed the way current interviewers expect:

  1. Component-based architecture — apps are trees of reusable components; standalone by default since v17, so NgModule is now optional.
  2. TypeScript-first — static typing, decorators, and strong tooling out of the box.
  3. Signals & reactivitysignal(), computed(), and effect() (stable since v20) give fine-grained reactivity and power zoneless change detection (default in new v21+ apps).
  4. Dependency injection — a hierarchical, typed DI system via inject() / providedIn.
  5. Built-in control flow@if/@for/@switch replace the old *ngIf/*ngFor.
  6. Powerful router — lazy loading, functional guards, and @defer for deferrable views.
  7. Forms, HttpClient, RxJS — first-class data and async handling.
  8. SSR/hydration@angular/ssr with incremental hydration.
  9. Angular CLI — scaffolding, build (esbuild/Vite), and test tooling.

The trap to avoid: leading with "two-way data binding" as a headline feature is AngularJS-era thinking. Modern Angular's headline is signals + standalone + zoneless.

↑ Back to top

Follow-up 1

Can you explain how TypeScript is used in Angular?

TypeScript is a statically typed superset of JavaScript that adds features like static typing, classes, and interfaces to JavaScript. Angular is built using TypeScript, and it is the recommended language for developing Angular applications.

TypeScript provides several benefits when used in Angular:

  1. Static typing: TypeScript allows you to specify the types of variables, function parameters, and return values. This helps in catching errors at compile-time and provides better tooling support.

  2. Classes and interfaces: TypeScript supports object-oriented programming features like classes and interfaces. This makes it easier to organize and structure your code.

  3. Decorators: TypeScript introduces decorators, which are a way to add metadata to classes, methods, and properties. Decorators are extensively used in Angular for various purposes, such as defining components, services, and routing configurations.

Overall, TypeScript enhances the development experience in Angular by providing better type checking, code organization, and tooling support.

Follow-up 2

What is the role of decorators in Angular?

Decorators are a feature of TypeScript that allow you to add metadata to classes, methods, and properties. In Angular, decorators play a crucial role in defining and configuring various aspects of an application.

Some of the common uses of decorators in Angular include:

  1. Component decorators: Angular uses decorators like @Component to define components. These decorators provide metadata about the component, such as its selector, template, styles, and dependencies.

  2. Service decorators: Decorators like @Injectable are used to define services in Angular. These decorators allow you to specify the dependencies of a service and configure its behavior.

  3. Directive decorators: Angular provides decorators like @Directive to define custom directives. These decorators allow you to specify the behavior of the directive and its interaction with the DOM.

  4. Module decorators: Decorators like @NgModule are used to define modules in Angular. These decorators allow you to configure the dependencies and components of a module.

Overall, decorators in Angular provide a way to add metadata and configure various aspects of an application, making it more modular and maintainable.

Follow-up 3

How does dependency injection work in Angular?

Dependency injection is a design pattern that allows you to manage dependencies between different components and services in an application. In Angular, dependency injection is a core feature that helps in building modular and maintainable applications.

In Angular, dependencies are defined as services, which are classes that provide specific functionality. These services can be injected into other components or services that depend on them.

The process of dependency injection in Angular involves the following steps:

  1. Service registration: Services are registered with the Angular dependency injection system. This can be done by adding the service class to the providers array of a module or by using the @Injectable decorator.

  2. Dependency resolution: When a component or service needs a dependency, Angular checks its dependency injection configuration to find the appropriate service. If the service is found, an instance of the service is created and injected into the component or service.

  3. Hierarchical injection: Angular uses a hierarchical injection system, where dependencies can be resolved at different levels. This allows for flexibility in managing dependencies and enables the creation of modular and reusable components.

Overall, dependency injection in Angular helps in decoupling components and services, making the application more flexible and easier to test.

2. How does Angular use TypeScript?

Angular is written in TypeScript and expects you to build apps in it. TypeScript adds static typing, interfaces, generics, enums, and decorators on top of JavaScript, which gives Angular three big wins: errors caught at compile time, rich editor tooling (autocomplete, safe refactors, inline docs), and self-documenting code via types.

Concretely, TypeScript shows up everywhere in Angular: decorators (@Component, @Injectable) attach metadata the compiler reads; typed dependency injection resolves services by their class type; and APIs like typed reactive forms, signals (signal()), and strongly-typed HttpClient responses all lean on the type system.

A worthwhile follow-up answer: Angular compiles templates ahead-of-time (AOT, the default), and TypeScript's type information helps the compiler check template bindings too (with strict template type-checking), catching mistakes like binding to a non-existent property before runtime.

↑ Back to top

Follow-up 1

What are the benefits of using TypeScript in Angular?

There are several benefits of using TypeScript in Angular:

  1. Type Safety: TypeScript adds static typing to JavaScript, which helps catch errors during development and provides better tooling support.
  2. Enhanced Tooling: TypeScript provides features like autocompletion, code navigation, and refactoring support in modern IDEs.
  3. Improved Code Organization: TypeScript supports features like classes, modules, and interfaces, which help in organizing and structuring code in a more maintainable way.
  4. Better Error Checking: TypeScript performs static type checking, which helps catch errors at compile-time rather than runtime.
  5. Compatibility with JavaScript: TypeScript is a superset of JavaScript, which means existing JavaScript code can be easily integrated into Angular projects.

Follow-up 2

Can you give an example of how TypeScript is used in Angular?

Sure! Here's an example of how TypeScript is used in Angular:

import { Component } from '@angular/core';

@Component({
  selector: 'app-example',
  template: `<h1>Hello, {{ name }}!</h1>`
})
export class ExampleComponent {
  name: string = 'John';
}

In this example, we define an Angular component using TypeScript. The name property is declared with a type annotation (string), which provides type safety and helps catch errors during development.

Follow-up 3

What is the difference between JavaScript and TypeScript in the context of Angular?

In the context of Angular, the main difference between JavaScript and TypeScript is that TypeScript is a statically typed superset of JavaScript. Here are some key differences:

  1. Static Typing: TypeScript adds static typing to JavaScript, which allows developers to declare types for variables, function parameters, and return values. This helps catch errors during development and provides better tooling support.
  2. Additional Features: TypeScript introduces additional features like classes, modules, interfaces, and decorators, which are not available in JavaScript. These features help in organizing and structuring code in a more maintainable way.
  3. Compilation Step: TypeScript code needs to be compiled to JavaScript before it can be executed in the browser. This compilation step is performed by the TypeScript compiler (tsc), which transpiles TypeScript code to JavaScript.
  4. Compatibility: TypeScript is a superset of JavaScript, which means existing JavaScript code can be easily integrated into TypeScript projects.

3. What are decorators in Angular and how are they used?

Decorators are functions that attach metadata to a class, method, property, or parameter, letting Angular know how to process it. They're written with the @ symbol immediately before the target. Angular reads this metadata at compile/runtime to wire the app together.

The ones you should be able to name:

  • Class decorators@Component, @Directive, @Injectable, @Pipe, and (legacy) @NgModule.
  • Property decorators@Input, @Output, @ViewChild, @HostBinding. (Note: in modern Angular many of these have signal-based equivalentsinput(), output(), viewChild() — that are preferred in new code.)
  • Method/parameter decorators@HostListener, and @Inject/@Optional/@Self for DI.

A current nuance worth mentioning: these are still TypeScript's experimental (legacy) decorators driven by Angular's compiler — not the TC39 standard decorators — and the team is gradually offering decorator-free, signal-based APIs (input(), model(), inject()) as the modern alternative.

↑ Back to top

Follow-up 1

Can you give an example of a decorator in Angular?

Sure! Here's an example of a decorator in Angular that adds logging functionality to a method:

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling method ${propertyKey} with arguments: ${args}`);
    const result = originalMethod.apply(this, args);
    console.log(`Method ${propertyKey} returned: ${result}`);
    return result;
  };
}

class MyClass {
  @log
  myMethod() {
    // Method implementation
  }
}

In this example, the log decorator is applied to the myMethod method of the MyClass class. When the myMethod is called, the decorator adds logging statements before and after the method execution.

Follow-up 2

What is the difference between a decorator and a normal function?

The main difference between a decorator and a normal function is that decorators are applied to classes, methods, properties, or parameters at design time, while normal functions are called at runtime. Decorators are used to modify or enhance the behavior of the target element, whereas normal functions are typically used to perform a specific task or computation.

Another difference is that decorators can access and modify the metadata of the target element, which can be useful for implementing features like dependency injection or component configuration in Angular.

Follow-up 3

How do decorators contribute to the modularity of Angular?

Decorators contribute to the modularity of Angular by allowing developers to separate concerns and add functionality to specific elements without modifying their original implementation. By applying decorators to classes, methods, properties, or parameters, developers can easily extend or modify the behavior of these elements without the need to modify their source code.

This modularity enables better code organization, reusability, and maintainability. It also promotes the use of design patterns like dependency injection and allows for the creation of reusable decorators that can be applied to multiple elements throughout an Angular application.

4. Can you explain the concept of dependency injection in Angular?

Dependency Injection (DI) is a design pattern where a class receives its dependencies from an external provider instead of constructing them itself. Angular has a built-in DI framework that does this for you, which keeps classes loosely coupled, easy to test (swap real services for mocks), and reusable.

How it works in Angular: you register a provider (most often @Injectable({ providedIn: 'root' })), and Angular's injector resolves the dependency by its type token when a component or service asks for it — either as a constructor parameter or via the inject() function:

@Injectable({ providedIn: 'root' })
export class DataService {}

export class UserComponent {
  private data = inject(DataService); // injector supplies the singleton
}

The detail interviewers reward: injectors are hierarchical (root → route → element), so where you provide a service controls its scope and lifetime; providedIn: 'root' yields a tree-shakable, app-wide singleton, while providing it on a component creates a new instance per component subtree.

↑ Back to top

Follow-up 1

What are the benefits of dependency injection?

There are several benefits of using dependency injection in Angular:

  1. Modularity: Dependency injection promotes modularity by allowing components to be easily replaced or modified without affecting other parts of the application.

  2. Reusability: Components and services can be reused in different parts of the application by injecting them wherever they are needed.

  3. Testability: Dependency injection makes it easier to write unit tests for components and services by allowing dependencies to be easily mocked or replaced with test doubles.

  4. Flexibility: With dependency injection, it is easy to change the implementation of a dependency without modifying the code that uses it.

  5. Maintainability: By decoupling dependencies, the code becomes easier to understand, maintain, and refactor.

Follow-up 2

Can you give an example of dependency injection in Angular?

Sure! Here's an example of how dependency injection works in Angular:

import { Component, Injectable } from '@angular/core';

@Injectable()
class DataService {
  getData() {
    return 'Data from the service';
  }
}

@Component({
  selector: 'app-example',
  template: `{{ data }}`
})
class ExampleComponent {
  constructor(private dataService: DataService) {}

  get data() {
    return this.dataService.getData();
  }
}

In this example, we have a DataService class that provides a method getData(). The ExampleComponent class depends on this service and injects it in its constructor. By doing so, the ExampleComponent can access the getData() method and display the data in its template.

Follow-up 3

How does dependency injection contribute to the testability of an Angular application?

Dependency injection greatly contributes to the testability of an Angular application in the following ways:

  1. Mocking Dependencies: With dependency injection, it is easy to mock dependencies during unit testing. This allows for isolated testing of components and services without relying on the actual implementation of their dependencies.

  2. Injecting Test Doubles: Dependency injection allows for injecting test doubles, such as spies or stubs, in place of actual dependencies. This enables fine-grained control over the behavior of dependencies during testing.

  3. Isolating Components: By injecting dependencies, components can be tested in isolation without the need for complex setup or interaction with external resources.

Overall, dependency injection makes it easier to write unit tests for Angular components and services, leading to more reliable and maintainable code.

5. How does Angular handle modularity?

Historically Angular organized code with NgModules (@NgModule) — containers that grouped components, directives, pipes, and providers and declared their dependencies. They worked but added boilerplate and a confusing extra layer for newcomers.

Modern Angular handles modularity primarily through standalone components (the default since v17). A standalone component declares its own imports directly, so the unit of modularity is the component itself — no NgModule required. You compose features by importing exactly what a component needs, which improves tree-shaking and makes lazy loading per-route trivial with loadComponent.

Be ready for the follow-up "are NgModules dead?" — not quite. They still exist and are fully supported for backward compatibility and incremental migration, and some older libraries still ship them. But for new code, standalone is the recommended default, and app-wide providers now live in bootstrapApplication(..., { providers: [...] }) rather than a root module.

↑ Back to top

Follow-up 1

What is the role of modules in Angular?

Modules in Angular play a crucial role in organizing and managing the application's components, directives, services, and other code. They provide a way to encapsulate related functionality into cohesive units. Modules also define the dependencies required by the application, such as other modules or external libraries. By importing and exporting modules, different parts of the application can easily share and reuse code, making it easier to develop and maintain complex applications.

Follow-up 2

Can you give an example of how modules are used in Angular?

Sure! Here's an example of how modules are used in Angular:

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  bootstrap: [AppComponent]
})
export class AppModule {}

In this example, the AppModule is the root module of the application. It imports the BrowserModule from @angular/platform-browser and declares the AppComponent. The BrowserModule provides essential services and directives for running Angular applications in the browser. The AppComponent is the main component of the application. By defining and importing modules like this, Angular can properly bootstrap and run the application.

Follow-up 3

How do modules contribute to the scalability of an Angular application?

Modules contribute to the scalability of an Angular application by providing a modular and organized structure. As the application grows, different modules can be created to encapsulate and manage specific features or sections of the application. This allows for better separation of concerns and makes it easier to maintain and extend the application. Modules also enable lazy loading, which means that modules can be loaded on-demand when needed, reducing the initial load time of the application. Additionally, modules can be easily shared and reused across different projects, promoting code reusability and reducing development time.

6. What are Angular Signals, and how do they differ from RxJS Observables?

Signals are Angular's built-in fine-grained reactivity primitive (stable since v20). A signal() holds a value, computed() derives from other signals (lazily, memoized), and effect() runs side effects when its dependencies change. Reading a signal in a template registers a dependency, so when it changes Angular updates only the views that read it.

count = signal(0);
double = computed(() =&gt; this.count() * 2);
increment() { this.count.update(n =&gt; n + 1); }

How they differ from Observables: signals are synchronous, always have a current value, and are pull-based (you call count() to read), making them ideal for component/UI state. Observables are push-based streams built for asynchronous events and time-based composition (HTTP, debouncing, websockets). They interoperate via toSignal() and toObservable(). The 2026 guidance: use signals for state, RxJS for async event streams — and signals are what make zoneless change detection efficient. Interviewers ask this to check your knowledge is current rather than stuck in the pre-signals era.

↑ Back to top

7. What is zoneless change detection and how does it relate to OnPush and signals?

Traditionally Angular used Zone.js to monkey-patch async APIs (events, timers, XHR) and trigger change detection broadly after anything async — simple but over-eager, and it adds ~70KB. Zoneless change detection (stabilized in v20.2, default for new apps in v21) removes Zone.js: Angular schedules change detection from explicit signals like signal updates, template event handlers, and markForCheck()/async-pipe emissions.

OnPush was the earlier optimization — it tells Angular to check a component only when its inputs change (by reference), an event fires in it, or an observable it uses via async emits. Zoneless takes this further app-wide, and signals are the bridge: a changed signal precisely notifies the components that read it, so updates are minimal and predictable. Enable it with provideZonelessChangeDetection(). A strong answer notes the migration path: adopt OnPush and signals first, which makes going zoneless low-risk.

↑ Back to top

Live mock interview

Mock interview: Key Features of Angular

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.