# Interfaces

## 1. What an Interface Is

An `interface` is a named description of an object's shape — the properties it must have and their types. It exists **only at compile time**; like everything else in the Type System Fundamentals topic, it's fully erased before runtime.

```ts
interface User {
  id: number;
  name: string;
  email: string;
}

function printUser(user: User) {
  console.log(`${user.name} <${user.email}>`);
}
```

## 2. Optional and Readonly Properties

```ts
interface Config {
  retries?: number;        // optional — may be omitted entirely
  readonly apiKey: string;  // can be set once, never reassigned after
}

const config: Config = { apiKey: "abc123" }; // retries omitted — fine
config.apiKey = "xyz789"; // ❌ error: apiKey is readonly
```

`readonly` is a compile-time-only restriction — like all TypeScript features, nothing stops mutation at runtime if the value is accessed through an untyped or `any` reference.

## 3. Method Signatures

```ts
interface Greeter {
  greet(name: string): string;        // method shorthand syntax
  farewell: (name: string) => string; // property holding a function type
}
```

Both describe a callable member, but they are checked slightly differently by the compiler (full detail in the "Under the Hood" section) — the shorthand `greet(...)` form is the more common, idiomatic style for interface methods.

## 4. Extending Interfaces

```ts
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

const rex: Dog = { name: "Rex", breed: "Labrador" }; // must satisfy BOTH shapes
```

An interface can extend **multiple** interfaces at once — combining every required member from each:

```ts
interface Swimmer { swim(): void; }
interface Flyer { fly(): void; }

interface Duck extends Swimmer, Flyer {
  quack(): void;
}
```

## 5. Declaration Merging

Unlike almost every other declaration in TypeScript, **multiple `interface` declarations with the same name automatically merge** into one combined interface, rather than causing a duplicate-declaration error.

```ts
interface Window {
  title: string;
}

interface Window {
  size: { width: number; height: number };
}

// merged result — Window now requires BOTH title and size
const w: Window = { title: "My App", size: { width: 800, height: 600 } };
```

![Two separate interface Window declarations merge into one combined shape requiring both title and size](diagrams/01-declaration-merging.svg)

This is intentional and widely used for **augmenting** types you don't own — e.g. adding a custom property to the global `Window` or `process.env` types from a library, without modifying that library's source.

## 6. Index Signatures

```ts
interface StringDictionary {
  [key: string]: string; // any string key maps to a string value
}

const colors: StringDictionary = { primary: "blue", secondary: "green" };
colors.tertiary = "red"; // ✅ any string key is valid
```

## 7. Interfaces vs Type Aliases (Preview)

Both can describe object shapes, and are interchangeable for most everyday use — the full comparison (including why interfaces merge and type aliases don't) is the Type Aliases topic. The short version: prefer `interface` for object shapes meant to be extended or augmented, and `type` for unions, tuples, and other non-object-shape constructs interfaces can't express at all.

## 8. Under the Hood: Merging and the Method-vs-Property Distinction

### Declaration merging is literal member-list concatenation

When the checker encounters two `interface Window` declarations, it doesn't pick one or error — it builds a single internal symbol for `Window` and **concatenates both declarations' member lists** into one combined shape, checked as a unit from then on. This is a real, deliberate compiler feature — not a coincidence of how modules work — which is why it works even across separate files compiled together (a common pattern: a library ships a base interface, and your project's own `.d.ts` file adds a merging declaration to extend it without touching the library's source).

```
interface Window { title: string }         →  member list: [title]
interface Window { size: {...} }             →  member list: [title, size]  (merged, not replaced)
```

### Methods are checked bivariantly; property function types are not

This is one of TypeScript's most subtle, deliberate design choices:

```ts
interface Handler {
  onEvent(e: MouseEvent): void;       // method shorthand — checked BIVARIANTLY
  onEventProp: (e: MouseEvent) => void; // property function type — checked STRICTLY (contravariantly)
}

class SpecificHandler implements Handler {
  onEvent(e: Event): void {}          // ✅ allowed — narrower parameter type, bivariant check permits it
  onEventProp = (e: Event) => {};      // ❌ error — strict function type checking rejects this
}
```

Structurally, accepting a *narrower* parameter type than required is technically unsound (a function expecting a specific `MouseEvent` might not handle every generic `Event`) — but TypeScript deliberately allows it for **method shorthand syntax specifically**, because this exact pattern (an override accepting a more specific event type than the base signature) is extremely common in real-world DOM/UI code, and strict-everywhere checking here would reject huge amounts of valid, safe-in-practice code. Property-syntax function types don't get this exception — they're checked with full strictness. This asymmetry is intentional, documented compiler behavior, not an inconsistency.

### Excess property checks apply to interfaces the same way

Since interfaces describe object shapes, they participate in the Type System Fundamentals topic's excess-property-check special case identically to inline object type annotations — a fresh object literal assigned directly to an interface-typed variable is checked for unknown properties; the same object passed through a variable first is not.

## 9. Common Pitfalls

1. **Expecting readonly to prevent runtime mutation** — it's compile-time only; `Object.freeze()` is the actual runtime enforcement mechanism, and the two are unrelated.
2. **Being surprised by declaration merging** — accidentally reusing an interface name in the same scope silently merges instead of erroring, which can hide what looks like a duplicate-declaration bug.
3. **Assuming method shorthand and property function types are equivalent** — the bivariance difference in section 8 means they behave differently for subtyping and `implements` checks, even though they look interchangeable at first glance.
4. **Forgetting `extends` combines requirements, not options** — an interface extending two others must satisfy the UNION of their required members, not either one alone.

## 10. Quick Reference

```
interface Name { prop: T; opt?: T; readonly ro: T; method(): T }
interface A extends B, C {}       → combines required members
interface X {} + interface X {}    → MERGE, not an error
[key: string]: T                    → index signature
```
