TypeScript Interfaces, From Syntax to interface vs type

An interface names the shape of an object: which properties it has and what type each holds. Mark a property optional with ? and unchangeable with readonly, build on another shape with extends, and reopen the same name to merge more members in. Interfaces leave no trace in the compiled JavaScript; they only guide the checker.

Interface syntax at a glance
You wantWrite
A required propertyname: string;
An optional propertynickname?: string;
A property that cannot be reassignedreadonly id: number;
A methodgreet(other: User): string;
Any number of string keys[key: string]: number;
Reuse another shapeinterface Admin extends User { ... }
Combine several shapesinterface A extends B, C { ... }

Updated

Add fields in the builder below, tick optional or readonly, and copy the interface or the equivalent type alias. The sections underneath cover extending, merging and the few cases where the choice between interface and type really matters.

Interface builder

interface Product {
  readonly id: number;
  name: string;
  price: number;
  tags?: string[];
}

interface vs type alias

What each declaration can do
Featureinterfacetype alias
Describe an object shapeYesYes
Add members by redeclaring the nameYes (merging)No, duplicate identifier error
Build on another shapeextendsIntersection with &
Union of shapes or literalsNoYes
Name a primitive or tupleNoYes
Mapped and conditional typesNoYes

Patterns you will write often

Extending one and several interfaces

Admin gets every User member plus its own; Card combines two unrelated shapes.

interface User {
  readonly id: number;
  name: string;
  email?: string;
}

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

interface Sized { width: number; height: number }
interface Labelled { label: string }
interface Card extends Sized, Labelled {}

const card: Card = { width: 320, height: 180, label: "Plan" };

Declaration merging on Window

Placed in a .d.ts file, this lets window.appVersion type check everywhere.

// globals.d.ts
export {};

declare global {
  interface Window {
    appVersion: string;
  }
}

Index signature and call signature

Scores accepts any string key; Validator describes a callable value.

interface Scores {
  [player: string]: number;
}

interface Validator {
  (value: string): boolean;
}

const scores: Scores = { ana: 12, ben: 9 };
const notEmpty: Validator = (v) => v.trim().length > 0;

extends versus intersections

interface Bear extends Animal copies every member of Animal and lets you add more. A type alias does the same job with an intersection, type Bear = Animal & { honey: boolean }.

The handbook notes that extends can be faster for the compiler and that it reports conflicting members right where they are declared. Intersections are handy for gluing together shapes you do not own.

Optional, readonly and index signatures

A question mark after the name makes a property optional, and its read type includes undefined. readonly blocks assignment to that property after creation but is a compile-time check only.

An index signature, [key: string]: number, describes dictionaries whose keys you cannot list in advance. Named properties in the same interface must fit the index signature's value type.

Merging to extend global and library types

Because interfaces merge, a declaration file can add your own fields to built-in shapes. A common case is giving Window a property that a script sets, so reading it type checks without a cast.

How it works

  1. Name the interface and, if it builds on another one, type that name in the extends box.
  2. Add a row per property, choosing its type and whether it is optional or readonly.
  3. Read the interface and the type alias the builder writes from the same rows.
  4. Copy either version into your code.

Tips

Frequently asked questions

Should I use interface or type?

For object shapes either works and the handbook calls it mostly personal preference, suggesting interface until you need a feature only type has. Use type for unions, tuples, primitives and mapped or conditional types; use interface when you want declaration merging.

What is declaration merging?

Declaring an interface with a name that already exists adds members to it instead of failing. Libraries rely on it so you can extend their types, for example adding a property to Window. A type alias with a repeated name is an error.

What happens if two merged interfaces give a property different types?

The compiler reports an error. With an intersection of two types the conflict is not reported at the declaration; the property becomes never, which surfaces later and is harder to trace.

Can an interface describe a function or an array?

Yes. A call signature such as (value: string): boolean describes a function, and an index signature such as [index: number]: string describes an array-like object. For simple cases a type alias like type Check = (v: string) => boolean reads more clearly.

Do interfaces exist at runtime?

No. They compile to nothing, so you cannot use instanceof with an interface or list its keys. Check values with a runtime guard and annotate its return type with the interface.

Related: TypeScript vs JavaScript, TypeScript enums, React with TypeScript, JSON to TypeScript converter.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap