AngularJSTypeScript for Angular

TypeScript for Angular

Angular is built with and for TypeScript — you cannot use Angular effectively without it. TypeScript is a statically-typed superset of JavaScript that compiles down to plain JavaScript.

This page covers the TypeScript features you will encounter most in Angular development. If you already know TypeScript well, use this as a quick reference. If you are new to TypeScript, work through each section carefully.

Why TypeScript in Angular?
  • Catch bugs at compile time rather than at runtime in the browser

  • Excellent IDE autocompletion and refactoring support

  • Decorators (@Component, @Injectable, @Input) — a core Angular mechanism

  • Interfaces and generics make component contracts explicit

  • Strict template type checking catches errors in HTML files

  • Self-documenting code — function signatures show what data they expect

Type Annotations

TypeScript adds type annotations to variables, parameters, and return values:

TS
// Basic types
let name: string = 'Angular';
let version: number = 17;
let isStable: boolean = true;
let tags: string[] = ['framework', 'typescript'];
let tuple: [string, number] = ['Angular', 17];

// Union types — value can be one of several types
let id: string | number = 'abc';
id = 42;  // also valid

// any — disables type checking (avoid in Angular)
let anything: any = 'hello';
anything = 42;  // no error, but dangerous

// unknown — safer than any — you must check the type before using
let value: unknown = getData();
if (typeof value === 'string') {
  console.log(value.toUpperCase());  // safe
}
Interfaces

Interfaces define the shape of an object. Angular uses them extensively for component inputs, API responses, and service contracts:

TS
interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';   // literal union type
  createdAt: Date;
  avatar?: string;    // optional property (the ? makes it optional)
  readonly apiKey: string;  // cannot be changed after creation
}

// Using the interface
function greetUser(user: User): string {
  return `Hello, ${user.name}! You are logged in as ${user.role}.`;
}

// TypeScript catches mistakes at compile time
const user: User = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  role: 'admin',
  createdAt: new Date(),
  apiKey: 'abc123',
};
Tip
In Angular, create a model file for each entity: user.model.ts, product.model.ts. This keeps your type definitions centralized and reusable.
Classes and Access Modifiers

Angular components and services are TypeScript classes. Access modifiers control visibility:

TS
class ProductService {
  // private — only accessible inside this class
  private apiUrl = 'https://api.example.com/products';

  // protected — accessible in this class and subclasses
  protected cache = new Map<number, Product>();

  // public (default) — accessible anywhere
  public loading = false;

  // readonly — can only be set once (in constructor or declaration)
  readonly maxRetries = 3;

  constructor(
    // Shorthand: declare + initialize in one step
    private http: HttpClient,
    public authService: AuthService,
  ) {}

  getProduct(id: number): Observable<Product> {
    return this.http.get<Product>(`${this.apiUrl}/${id}`);
  }
}
Warning
In Angular templates, you can only access public properties and methods of a component class. Private and protected members are not accessible in templates.
Generics

Generics let you write reusable code that works with different types. Angular uses generics extensively in Observable&lt;T&gt;, EventEmitter&lt;T&gt;, and HttpClient.get&lt;T&gt;:

TS
// Generic function
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

const firstNumber = first([1, 2, 3]);   // type: number
const firstString = first(['a', 'b']);  // type: string

// Generic interface
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// Usage in Angular
interface Product { id: number; name: string; price: number; }

// HttpClient.get<T> — the T tells TypeScript what the response shape is
this.http.get<ApiResponse<Product[]>>('/api/products')
  .subscribe(response => {
    // response.data is typed as Product[]
    this.products = response.data;
  });
Decorators

Decorators are a TypeScript feature that Angular uses heavily. They are functions that modify classes, methods, properties, or parameters by attaching metadata.

Angular's most important decorators:

Decorator

Applied To

Purpose

@Component

Class

Marks a class as an Angular component

@Injectable

Class

Marks a class as injectable via DI

@NgModule

Class

Marks a class as an NgModule

@Directive

Class

Marks a class as a directive

@Pipe

Class

Marks a class as a pipe

@Input()

Property

Declares an input property (parent → child data)

@Output()

Property

Declares an output property (child → parent events)

@ViewChild()

Property

Gets a reference to a child component or DOM element

@HostListener()

Method

Listens to events on the host element

TS
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';

@Component({
  selector: 'app-rating',
  standalone: true,
  template: `...`,
})
export class RatingComponent implements OnInit {
  @Input() maxStars = 5;
  @Input({ required: true }) initialRating!: number;  // required input
  @Output() ratingChange = new EventEmitter<number>();

  currentRating = 0;

  ngOnInit() {
    this.currentRating = this.initialRating;
  }

  setRating(stars: number) {
    this.currentRating = stars;
    this.ratingChange.emit(stars);
  }
}
Enums

Enums are named constants — useful for values like status codes, user roles, and event types:

TS
// Numeric enum (default)
enum Direction {
  Up,       // 0
  Down,     // 1
  Left,     // 2
  Right,    // 3
}

// String enum (preferred — more readable in logs and debugging)
enum UserRole {
  Admin = 'ADMIN',
  Editor = 'EDITOR',
  Viewer = 'VIEWER',
}

// Const enum — inlined at compile time (zero runtime overhead)
const enum Status {
  Active = 'active',
  Inactive = 'inactive',
  Pending = 'pending',
}

// Usage in an Angular component
interface User {
  name: string;
  role: UserRole;
}

function canEdit(user: User): boolean {
  return user.role === UserRole.Admin || user.role === UserRole.Editor;
}
Type Aliases

TS
// Type alias for a union type
type ID = string | number;

// Type alias for a function signature
type EventHandler<T> = (event: T) => void;

// Type alias for a complex object — similar to interface but can use unions
type Theme = 'light' | 'dark' | 'system';

type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'ghost';

// Mapped types — transform all properties
type Partial<T> = { [K in keyof T]?: T[K] };          // all properties optional
type Readonly<T> = { readonly [K in keyof T]: T[K] }; // all properties readonly

// Utility types Angular developers use often
type UserUpdate = Partial<User>;           // all User fields optional
type ReadonlyUser = Readonly<User>;        // all User fields readonly
type UserPreview = Pick<User, 'id' | 'name'>;  // only id and name
type UserWithoutPassword = Omit<User, 'password'>;  // everything except password
Optional Chaining and Nullish Coalescing

These two operators are critical in Angular templates and services where data might be null or undefined:

TS
const user = getUserById(1);

// Optional chaining (?.) — stops evaluation if value is null/undefined
const city = user?.address?.city;       // undefined if user or address is null
const firstTag = user?.tags?.[0];       // safe array access

// Nullish coalescing (??) — returns right side if left is null/undefined
const displayName = user?.name ?? 'Anonymous';
const pageSize = config?.pageSize ?? 10;

// Combining both
const userRole = user?.profile?.role ?? 'guest';

// Non-null assertion (!) — tells TypeScript "trust me, this is not null"
// Use sparingly — only when you are 100% certain
const element = document.getElementById('root')!;  // asserts not null
Note
Angular templates have a safe navigation operator ?. built in: {{ user?.address?.city }}. This prevents template errors when data is loading.
Async/Await and Promises

TS
// Traditional Promise chain
fetch('/api/users')
  .then(response => response.json())
  .then(users => console.log(users))
  .catch(error => console.error(error));

// Async/await — cleaner syntax
async function loadUsers(): Promise<User[]> {
  try {
    const response = await fetch('/api/users');
    if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
    return await response.json() as User[];
  } catch (error) {
    console.error('Failed to load users:', error);
    return [];
  }
}

// In Angular, you'll mostly use Observables (RxJS) rather than Promises
// But async/await is useful for one-time operations
async ngOnInit(): Promise<void> {
  this.users = await this.userService.getUsersOnce();
}
TypeScript with Angular Components — Putting It Together

TS
import { Component, Input, Output, EventEmitter, signal, computed } from '@angular/core';

// Interfaces for the component's data
interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

type SortField = 'name' | 'price' | 'quantity';

@Component({
  selector: 'app-cart',
  standalone: true,
  template: `
    <div>
      <h2>Shopping Cart ({{ itemCount() }} items)</h2>
      <p>Total: {{ total() | currency }}</p>
      @for (item of items(); track item.id) {
        <div>{{ item.name }} x{{ item.quantity }} = {{ item.price * item.quantity | currency }}</div>
      }
    </div>
  `,
})
export class CartComponent {
  // Signal-based reactive state
  private cartItems = signal<CartItem[]>([]);

  // Computed values derived from state
  items = this.cartItems.asReadonly();
  itemCount = computed(() => this.cartItems().reduce((sum, item) => sum + item.quantity, 0));
  total = computed(() => this.cartItems().reduce((sum, item) => sum + item.price * item.quantity, 0));

  // Typed input and output
  @Input() set initialItems(items: CartItem[]) {
    this.cartItems.set(items);
  }

  @Output() checkout = new EventEmitter<CartItem[]>();

  addItem(item: CartItem): void {
    this.cartItems.update(items => {
      const existing = items.find(i => i.id === item.id);
      if (existing) {
        return items.map(i => i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i);
      }
      return [...items, { ...item, quantity: 1 }];
    });
  }

  onCheckout(): void {
    this.checkout.emit(this.cartItems());
  }
}
TypeScript Features Used Most in Angular

Feature

Angular Usage

Interfaces

Model shapes (User, Product), @Input types

Access modifiers (private/public)

Service methods, component properties

Generics

Observable<T>, EventEmitter<T>, HttpClient.get<T>

Decorators

@Component, @Injectable, @Input, @Output

Optional chaining (?.)

Safe template expressions, API responses

Nullish coalescing (??)

Default values when data may be absent

Type aliases

Union types for literal values (role: admin | user)

Enums

Constants for status, roles, event types

Readonly

Immutable state signals, configuration objects

async/await

One-time async operations, route resolvers