TS

Interfaces

TypeScript · 7 entries

Interface Declaration

syntax
interface Name {
  property: Type;
  method(param: Type): ReturnType;
}
example
interface Product {
  id: string;
  name: string;
  price: number;
  getDisplayPrice(): string;
}

const laptop: Product = {
  id: "prod_001",
  name: "Developer Laptop",
  price: 1299,
  getDisplayPrice() {
    return `$${this.price.toFixed(2)}`;
  },
};
output
// laptop.getDisplayPrice() → "$1299.00"

Note Interfaces define the shape of objects. They are structurally typed — any object with the right shape satisfies the interface, no explicit 'implements' needed for plain objects.

Optional Properties

syntax
interface Name {
  required: Type;
  optional?: Type;
}
example
interface NotificationConfig {
  title: string;
  body: string;
  icon?: string;
  durationMs?: number;
  onDismiss?: () => void;
}

function showNotification(config: NotificationConfig) {
  const duration = config.durationMs ?? 3000;
  console.log(`Showing: ${config.title} for ${duration}ms`);
}
output
// Optional props have type T | undefined

Note An optional property (prop?) means 'may be missing or undefined'. This is different from prop: Type | undefined, which requires the key to be present. With exactOptionalPropertyTypes in tsconfig, this distinction is enforced even more strictly.

Readonly Properties

syntax
interface Name {
  readonly property: Type;
}
example
interface DatabaseRecord {
  readonly id: string;
  readonly createdAt: Date;
  name: string;
  updatedAt: Date;
}

const record: DatabaseRecord = {
  id: "rec_abc",
  createdAt: new Date(),
  name: "Initial",
  updatedAt: new Date(),
};

record.name = "Updated"; // OK
// record.id = "rec_xyz"; // Error: Cannot assign to 'id' because it is read-only
output
// readonly prevents reassignment at compile time only

Note readonly is a compile-time constraint — there is no runtime enforcement. Deep objects with readonly props can still have their nested properties mutated unless those are also readonly. Use Readonly<T> utility to make all properties readonly at once.

Extending Interfaces

syntax
interface Child extends Parent {
  additionalProp: Type;
}
interface Child extends ParentA, ParentB { ... }
example
interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface SoftDeletable {
  deletedAt: Date | null;
}

interface BlogPost extends Timestamped, SoftDeletable {
  title: string;
  content: string;
  authorId: string;
}

// BlogPost has: title, content, authorId, createdAt, updatedAt, deletedAt
output
// Multiple inheritance via comma-separated parent interfaces

Note An interface can extend multiple other interfaces. If parent interfaces have conflicting property types, you get a compile error. Interfaces can also extend type aliases (as long as the alias resolves to an object shape).

Implementing Interfaces

syntax
class ClassName implements InterfaceName { ... }
example
interface Logger {
  info(message: string): void;
  error(message: string, context?: Record<string, unknown>): void;
}

class ConsoleLogger implements Logger {
  info(message: string): void {
    console.log(`[INFO] ${message}`);
  }

  error(message: string, context?: Record<string, unknown>): void {
    console.error(`[ERROR] ${message}`, context ?? "");
  }
}
output
// Class must define all properties/methods declared in the interface

Note implements does NOT add types to the class — it only checks that the class satisfies the shape. You must still annotate parameter types in the class methods. A class can implement multiple interfaces with comma separation.

Index Signatures

syntax
interface Name {
  [key: string]: Type;
  [index: number]: Type;
}
example
interface TranslationMap {
  [locale: string]: string;
}

const greetings: TranslationMap = {
  en: "Hello",
  es: "Hola",
  ja: "こんにちは",
};

// Mixed: known + dynamic keys
interface AppConfig {
  appName: string;
  version: string;
  [key: string]: string; // all values must be string
}
output
// greetings["fr"] is string (even if missing at runtime)

Note Index signatures allow any key of the given type. When combined with named properties, all named property types must be compatible with the index signature type. Enable noUncheckedIndexedAccess in tsconfig to get T | undefined instead of just T for indexed access — much safer.

Declaration Merging

syntax
interface Name { propA: Type; }
interface Name { propB: Type; }
// Merged into one interface with both props
example
// Original library type
interface Window {
  title: string;
}

// Your augmentation — merges with the above
interface Window {
  analyticsId: string;
}

// Now Window has both title and analyticsId
const w: Window = {
  title: "My App",
  analyticsId: "UA-12345",
};
output
// Both declarations merge into a single interface

Note Declaration merging is unique to interfaces — type aliases cannot be re-opened. This is powerful for extending third-party types (like adding properties to Window or Express Request). The merged properties must not conflict in type.