# Interfaces and Type Aliases

> Source: https://learn-typescript.org/interfaces-and-type-aliases/
> Part of Learn TypeScript, free to read.

Once you are past primitives, most typing is describing the shape of objects. There are two ways to name a shape, and they overlap almost entirely.

```typescript
interface User {
  id: string;
  email: string;
  age: number;
}

type Product = {
  id: string;
  name: string;
  priceCents: number;
};
```

Both work the same way at the point of use:

```typescript
const ada: User = { id: "1", email: "ada@example.com", age: 36 };

const wrong: User = { id: "1", email: "ada@example.com" };
// Property 'age' is missing in type '{ id: string; email: string; }'
```

## Optional and readonly

```typescript
interface User {
  id: string;
  email: string;
  age?: number;            // may be absent — type is number | undefined
  readonly createdAt: Date; // cannot be reassigned after construction
}

const u: User = { id: "1", email: "a@b.com", createdAt: new Date() };
u.createdAt = new Date();  // error: Cannot assign to 'createdAt'
```

`readonly` is shallow — it stops reassignment of the property, not mutation of the object it points at.

## Nested and array properties

```typescript
interface Order {
  id: string;
  customer: User;                    // another named shape
  lines: OrderLine[];                // an array of them
  metadata: Record<string, string>;  // arbitrary string keys
  status: "pending" | "shipped";     // a union of literals
}
```

`Record<K, V>` is the idiomatic way to say "an object used as a lookup". The longhand is an index signature:

```typescript
interface Lookup {
  [key: string]: number;
}
```

:::warn An index signature is looser than it looks
`Lookup["anything"]` is typed as `number`, even for a key that does not exist — so you get `undefined` at runtime with no warning. Turn on `noUncheckedIndexedAccess` and the type becomes `number | undefined`, forcing you to check. It is the single most valuable compiler flag for catching real bugs.
:::

## Composition

Interfaces extend:

```typescript
interface Entity {
  id: string;
  createdAt: Date;
}

interface User extends Entity {
  email: string;
}
```

Type aliases intersect, which achieves the same thing:

```typescript
type Entity = { id: string; createdAt: Date };
type User = Entity & { email: string };
```

Both compose several sources:

```typescript
interface Admin extends Entity, Auditable { role: "admin" }
type Admin = Entity & Auditable & { role: "admin" };
```

## Where they genuinely differ

**Only `type` can express a union**, which is why it is the more common default:

```typescript
type Status = "pending" | "shipped" | "cancelled";
type Id = string | number;
type Handler = (e: Event) => void;      // and function types read better
```

**Only `interface` supports declaration merging** — two declarations with the same name combine:

```typescript
interface Window { myApp: AppState }     // adds to the existing DOM Window
```

That is essential for augmenting types from a library you do not control, and a footgun everywhere else, because a name can be extended from anywhere.

:::verdict Which to use
Use `type` by default — it covers unions, functions and object shapes uniformly. Use `interface` when you need declaration merging (augmenting a third-party type) or when you are publishing a type others will extend. Pick one convention per codebase and write it in your instructions file, otherwise generated code will use both interchangeably.
:::

## Structural typing

TypeScript checks shapes, not names. Anything with the right properties satisfies the type:

```typescript
interface Point { x: number; y: number }

function distance(p: Point): number {
  return Math.hypot(p.x, p.y);
}

const anything = { x: 3, y: 4, label: "extra" };
distance(anything);       // fine — it has x and y
```

This is usually what you want, and it is occasionally not:

```typescript
type UserId = string;
type OrgId = string;

function loadUser(id: UserId) {}
loadUser(someOrgId);      // compiles. wrong. both are just strings.
```

Two types with the same underlying shape are interchangeable — which is exactly the bug branded types solve, covered later in the track.

:::tip Excess property checks
```typescript
distance({ x: 3, y: 4, label: "extra" });   // ERROR here
```
Passing an object *literal* directly triggers an extra check that rejects unknown properties, on the reasoning that a property you wrote inline and that is not in the type is probably a typo. Assign it to a variable first and it is allowed. Surprising the first time, useful once you know.
:::

## Exercise

```typescript
// Model a blog post:
//   - id (string, readonly), title (string), body (string)
//   - tags: an array of strings
//   - publishedAt: a Date that may be absent (drafts)
//   - author: a nested shape with name and email
// Then write a `summarize(post)` function returning `"title — N tags"`.

// write your code here
```

## Common questions

### interface or type — really, which?

`type` unless you need declaration merging. It handles unions, function types and object shapes with one keyword, which means fewer decisions. The important part is consistency: a codebase using both at random is harder to read than either choice.

### Why was my object with extra properties rejected?

Excess property checking, which only applies to object literals passed directly. TypeScript assumes an unknown property written inline is a typo. Assigning to a variable first bypasses it, because at that point the object has a known type and the extra property is deliberate.

### How do I make a type where all properties are optional?

`Partial<T>`. There is a set of built-in utility types — `Partial`, `Required`, `Readonly`, `Pick`, `Omit` — covered in their own lesson later in this track.
