TypeScript Essentials

TypeScript Types and Interfaces

Master TypeScript interfaces, type aliases, generics, and utility types — essential patterns for building type-safe Angular applications.

Interfaces

Interfaces define the shape of an object. They are heavily used in Angular for defining component inputs, service responses, and data models.

Basic Interface

typescript
interface User {
  id: number;
  name: string;
  email: string;
  isActive: boolean;
}

const user: User = {
  id: 1,
  name: 'John Doe',
  email: 'john@example.com',
  isActive: true,
};

Optional and Readonly Properties

typescript
interface Product {
  readonly id: number;      // Cannot be changed after creation
  name: string;
  description?: string;     // Optional
  price: number;
  tags?: string[];           // Optional
}

const product: Product = { id: 1, name: 'Widget', price: 29.99 };
product.name = 'Super Widget';  // OK
product.id = 2;                  // Error: readonly

Extending Interfaces

typescript
interface Person {
  name: string;
  age: number;
}

interface Employee extends Person {
  employeeId: string;
  department: string;
}

interface Manager extends Employee {
  directReports: Employee[];
}

const manager: Manager = {
  name: 'Alice',
  age: 35,
  employeeId: 'E-001',
  department: 'Engineering',
  directReports: [],
};

Interface for Functions

typescript
interface SearchFunction {
  (query: string, limit?: number): Promise<string[]>;
}

const search: SearchFunction = async (query, limit = 10) => {
  // implementation
  return ['result1', 'result2'];
};

Type Aliases vs Interfaces

Both can define object shapes, but they have differences:

typescript
// Type alias
type Point = {
  x: number;
  y: number;
};

// Interface
interface IPoint {
  x: number;
  y: number;
}
FeatureInterfaceType Alias
Extend/Implementextends keywordIntersection (&)
Declaration merging✅ Yes❌ No
Union types❌ No✅ Yes
Primitives/tuples❌ No✅ Yes
Use in AngularPreferred for modelsPreferred for unions

When to Use What

typescript
// Use interface for object shapes (Angular services, models)
interface ApiResponse {
  data: unknown;
  status: number;
  message: string;
}

// Use type for unions, intersections, primitives
type Theme = 'light' | 'dark' | 'system';
type Nullable<T> = T | null;
type Coordinate = [number, number];

Generics

Generics let you write reusable code that works with any type while maintaining type safety.

Generic Functions

typescript
function identity<T>(value: T): T {
  return value;
}

const str = identity<string>('hello');  // string
const num = identity<number>(42);        // number
const auto = identity('inferred');       // string (inferred)

Generic Interfaces

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

// Usage with different data types
type UserResponse = ApiResponse<User>;
type ProductListResponse = ApiResponse<Product[]>;

const response: ApiResponse<User> = {
  data: { id: 1, name: 'John', email: 'j@test.com', isActive: true },
  status: 200,
  message: 'Success',
  timestamp: new Date(),
};

Generic Constraints

Limit what types can be used with a generic:

typescript
interface HasId {
  id: number;
}

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

// Works with any type that has an 'id' property
const users: User[] = [/* ... */];
const found = findById(users, 1); // Type is User | undefined

Multiple Generic Parameters

typescript
function mapArray<T, U>(items: T[], transform: (item: T) => U): U[] {
  return items.map(transform);
}

const names = mapArray([1, 2, 3], (n) => `Item ${n}`);
// Type: string[]

Utility Types

TypeScript provides built-in utility types that transform existing types:

Partial<T>

Makes all properties optional:

typescript
interface User {
  name: string;
  email: string;
  age: number;
}

// All properties become optional
function updateUser(id: number, updates: Partial<User>) {
  // updates can have any combination of User properties
}

updateUser(1, { name: 'New Name' });         // OK
updateUser(1, { email: 'new@email.com' });   // OK
updateUser(1, {});                             // OK

Pick<T, K> and Omit<T, K>

typescript
// Only selected properties
type UserPreview = Pick<User, 'name' | 'email'>;
// { name: string; email: string }

// All except specified properties
type UserWithoutAge = Omit<User, 'age'>;
// { name: string; email: string }

Record<K, V>

Create an object type with specific key/value types:

typescript
type Role = 'admin' | 'editor' | 'viewer';

const permissions: Record<Role, string[]> = {
  admin: ['read', 'write', 'delete'],
  editor: ['read', 'write'],
  viewer: ['read'],
};

Required<T> and Readonly<T>

typescript
interface Config {
  host?: string;
  port?: number;
}

// All properties required
type RequiredConfig = Required<Config>;
// { host: string; port: number }

// All properties readonly
type FrozenConfig = Readonly<Config>;

Common Angular Patterns

Component Input Types

typescript
interface TableColumn<T> {
  key: keyof T;
  header: string;
  sortable?: boolean;
  formatter?: (value: T[keyof T]) => string;
}

interface UserRow {
  name: string;
  email: string;
  role: string;
}

const columns: TableColumn<UserRow>[] = [
  { key: 'name', header: 'Name', sortable: true },
  { key: 'email', header: 'Email' },
  { key: 'role', header: 'Role', formatter: (v) => String(v).toUpperCase() },
];

Service Response Typing

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

// Service method
class UserService {
  getUsers(page: number): Observable<PaginatedResponse<User>> {
    // ...
  }
}

Next Steps

With a solid understanding of types and interfaces, you're ready to learn about classes and decorators — the foundation of Angular's component and service architecture.