Skip to content
intermediate Phase 2 · TypeScript Refresher for Angular

Generics and Utility Types

Keep type information flowing through functions, services, and helpers.

45m
0 problems
Topic Progress 0%

Generics for Type-Safe Reuse

Generics let you write functions, classes, and interfaces that work with multiple types while preserving type information.

Generic Functions

function first<T>(items: T[]): T | undefined {
  return items[0];
}

const num = first([1, 2, 3]);       // number | undefined
const str = first(['a', 'b']);      // string | undefined

Generic Interfaces

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

interface PaginatedResponse<T> {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
}

Generic Classes

class BehaviorSubject<T> {
  private value: T;

  constructor(initial: T) {
    this.value = initial;
  }

  getValue(): T {
    return this.value;
  }

  next(value: T): void {
    this.value = value;
  }
}

const subject = new BehaviorSubject<string>('hello');
subject.next('world');
const current: string = subject.getValue();

Generic Constraints

interface HasId {
  id: number;
}

function findById<T extends HasId>(items: T[], id: number): T | undefined {
  return items.find(item => item.id === id);
}

interface User extends HasId {
  name: string;
}

const users: User[] = [{ id: 1, name: 'Alice' }];
const user = findById(users, 1); // User | undefined

Multiple Type Parameters

function pair<K, V>(key: K, value: V): [K, V] {
  return [key, value];
}

const entry = pair('name', 'Alice'); // [string, string]

Utility Types for Reshaping Types

TypeScript provides built-in utility types that transform existing types. These are heavily used in Angular for component inputs, form models, and API responses.

Common Utility Types

interface User {
  id: number;
  name: string;
  email: string;
  role: string;
  createdAt: Date;
}

// Partial — all properties optional
type UserUpdate = Partial<User>;
// { id?: number; name?: string; ... }

// Pick — select specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }

// Omit — exclude specific properties
type UserWithoutMeta = Omit<User, 'createdAt' | 'role'>;
// { id: number; name: string; email: string; }

// Record — map keys to values
type UserRoles = Record<string, 'admin' | 'editor' | 'viewer'>;
// { [key: string]: 'admin' | 'editor' | 'viewer' }

// Required — all properties required
type UserRequired = Required<UserUpdate>;

// Readonly — all properties readonly
type FrozenUser = Readonly<User>;

Utility Types in Angular Services

@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('/api/users');
  }

  getUser(id: number): Observable<User> {
    return this.http.get<User>(`/api/users/${id}`);
  }

  updateUser(id: number, changes: Partial<User>): Observable<User> {
    return this.http.patch<User>(`/api/users/${id}`, changes);
  }

  createUser(data: Omit<User, 'id' | 'createdAt'>): Observable<User> {
    return this.http.post<User>('/api/users', data);
  }
}

Generic EventEmitter and Observable

@Component({
  selector: 'app-user-list',
  template: `...`,
})
export class UserListComponent {
  @Output() userSelected = new EventEmitter<User>();
  @Output() userDeleted = new EventEmitter<number>();

  users$ = new BehaviorSubject<User[]>([]);
  loading$ = new BehaviorSubject<boolean>(false);
}

Conditional Types

type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>;  // true
type B = IsString<42>;       // false

type NonNullable<T> = T extends null | undefined ? never : T;
type C = NonNullable<string | null>; // string

Quiz

1. What does the Partial<T> utility type do?

Question 1 options

2. What does Pick<T, K> do?

Question 2 options

3. How do you type an EventEmitter that emits a User object?

Question 3 options

Flashcards

Question

What are generics in TypeScript?

Answer

Type parameters that let functions, classes, and interfaces work with multiple types while preserving type information.

Question

What does Partial<User> create?

Answer

A type where all User properties are optional. Useful for update operations.

Question

How do you type an Observable from an HTTP GET that returns User[]?

Answer

this.http.get<User[]>('/api/users') — the generic parameter types the response body.

Revision Notes

Key Takeaways

  • 1. Generics preserve type information across reusable code
  • 2. Partial, Pick, and Omit reshape existing types
  • 3. EventEmitter<T> and Observable<T> provide type-safe events
  • 4. HTTP client methods use generics to type response bodies

Interview Tips

  • • Explain how generics prevent 'any' from leaking through your codebase
  • • Be ready to demonstrate utility types for common Angular patterns
  • • Mention that generics are erased at runtime — they are compile-time only

Cheat Sheet

Generics and Utility Types Cheat Sheet

Generic function:

function first<T>(items: T[]): T | undefined {
  return items[0];
}

Utility types:

  • Partial<T> — all optional
  • Required<T> — all required
  • Pick<T, K> — select keys
  • Omit<T, K> — exclude keys
  • Record<K, V> — key-value map
  • Readonly<T> — all readonly

Angular HTTP with generics:

this.http.get<User[]>('/api/users')
this.http.post<User>('/api/users', data)
this.http.patch<User>(`/api/users/${id}`, changes)

Continue Learning