home / skills / flpbalada / my-opencode-config / typescript-interface-vs-type

typescript-interface-vs-type skill

/skills/typescript-interface-vs-type

This skill helps you decide when to use interface versus type in TypeScript to improve type safety and maintainability.

npx playbooks add skill flpbalada/my-opencode-config --skill typescript-interface-vs-type

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
2.9 KB
---
name: typescript-interface-vs-type
description: Guides when to use interface vs type in TypeScript. Use this skill when defining object types, extending types, or choosing between interface and type aliases.
---

# TypeScript: Interface vs Type

## Core Principle

**Use `interface` until you need features from `type`.**

This is the official TypeScript recommendation from the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html).

## When to Use Interface

Use `interface` for:
- Object type definitions
- Extending other object types
- Class implementations
- Declaration merging (augmenting existing types)

## When to Use Type

Use `type` only when you need:
- Union types: `type Status = 'pending' | 'approved' | 'rejected'`
- Mapped types: `type Readonly<T> = { readonly [K in keyof T]: T[K] }`
- Conditional types: `type NonNullable<T> = T extends null | undefined ? never : T`
- Tuple types: `type Point = [number, number]`
- Function types (though interface can also work): `type Handler = (event: Event) => void`

## Prefer `interface extends` Over Intersection (`&`)

When extending object types, always prefer `interface extends` over type intersections.

```typescript
// Preferred
interface User {
  name: string;
}

interface Admin extends User {
  permissions: string[];
}

// Avoid
type User = {
  name: string;
};

type Admin = User & {
  permissions: string[];
};
```

### Reason 1: Better Error Messages

With `interface extends`, TypeScript raises errors at the definition when extending with incompatible properties:

```typescript
interface Base {
  id: number;
}

// Error immediately at definition
interface Extended extends Base {
  id: string; // Error: Type 'string' is not assignable to type 'number'
}
```

With intersections, errors only appear when accessing the incompatible property, making bugs harder to catch:

```typescript
type Base = {
  id: number;
};

// No error at definition
type Extended = Base & {
  id: string;
};

// Error only when used
const item: Extended = { id: 'abc' }; // Error appears here, not at type definition
```

### Reason 2: Better TypeScript Performance

`interface extends` provides better TypeScript performance:
- Interfaces are cached by name - TypeScript computes the type once and reuses it
- Intersections are recomputed every time they're used, which slows down type checking with complex types

See [TypeScript Performance Wiki](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections) for details.

## References

- [TypeScript Handbook - Everyday Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)
- [TypeScript Performance Wiki](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections)
- [Total TypeScript - Intersections vs Interface Extends](https://www.totaltypescript.com/books/total-typescript-essentials/objects#intersections-vs-interface-extends)

Overview

This skill guides when to choose TypeScript's interface versus type alias. It summarizes recommended defaults, key differences, and practical rules for defining object shapes, extending types, and using advanced type features. Follow these guidelines to improve type clarity, error detection, and compiler performance.

How this skill works

The skill inspects common type-design scenarios and recommends interface by default for object shapes and extension. It highlights cases where type aliases are required (unions, mapped, conditional, tuple, or certain function shapes). It also prefers interface extends over type intersections for clearer errors and faster type-checking.

When to use it

  • Use interface for plain object type definitions and class implementations.
  • Use interface when you need declaration merging or repeated augmentation.
  • Use type when you require unions, mapped types, conditional types, or tuples.
  • Use type for complex function signatures when more convenient than interface.
  • Prefer interface extends when creating derived object types rather than using & intersection.

Best practices

  • Default to interface for object shapes; switch to type only for features interfaces can't express.
  • Prefer interface extends over type intersections to catch incompatible properties at definition time.
  • Avoid unnecessary intersections; they can degrade TypeScript performance on large codebases.
  • Use named interfaces for reuse and clearer error messages; keep type aliases for compositional or computed types.
  • Keep unions and conditional logic in type aliases to make intent explicit and maintainable.

Example use cases

  • Define a reusable object contract implemented by classes: use interface.
  • Create a discriminated union of request states: use type with union literals.
  • Build a readonly version of an API response: use a mapped type via type alias.
  • Extend a user object into an admin with extra permissions: prefer interface extends.
  • Model tuple-based coordinates or fixed-length arrays: use type alias for the tuple.

FAQ

Can interfaces express unions or conditional types?

No. Interfaces cannot represent union, conditional, or mapped types. Use type aliases when those features are needed.

Why prefer interface extends over type & intersection?

Interface extends reports incompatibilities at type definition time and generally yields better TypeScript performance because interfaces are cached and reused.