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.
| You want | Write |
|---|---|
| A required property | name: string; |
| An optional property | nickname?: string; |
| A property that cannot be reassigned | readonly id: number; |
| A method | greet(other: User): string; |
| Any number of string keys | [key: string]: number; |
| Reuse another shape | interface Admin extends User { ... } |
| Combine several shapes | interface 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 Product {
readonly id: number;
name: string;
price: number;
tags?: string[];
}| Feature | interface | type alias |
|---|---|---|
| Describe an object shape | Yes | Yes |
| Add members by redeclaring the name | Yes (merging) | No, duplicate identifier error |
| Build on another shape | extends | Intersection with & |
| Union of shapes or literals | No | Yes |
| Name a primitive or tuple | No | Yes |
| Mapped and conditional types | No | Yes |
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" };Placed in a .d.ts file, this lets window.appVersion type check everywhere.
// globals.d.ts
export {};
declare global {
interface Window {
appVersion: string;
}
}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;
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.
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.
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.
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.
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.
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.
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.
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.
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.