learn
TypeScript

Objects: interface versus type

Optional and readonly fields, index signatures, and the classic question.

Objects are where TypeScript earns its keep: one shape declaration protects every function that touches an API user. Interviewers ask "interface versus type?" on almost every loop — the answer is short, and below.

Real-life situation

An API returns users with optional emails and occasional extra fields. Without a declared shape, every consumer re-guesses: is email always there? With interface ApiUser, the optionality lives in one place, and a misspelled extra property fails immediately instead of silently passing undefined downstream.

Visual

Two spellings, one shape
BrowserAPIRealtimeCacheDatabaseShareddurableephemeral

Try it

An API user with optional fields

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
interface ApiUser {
id: number;
name: string;
email?: string;
}
function label(user: ApiUser): string {
const suffix = user.email === undefined ? "" : " <" + user.email + ">";
return user.name + suffix;
}
const reader: ApiUser = { id: 1, name: "sam" };
console.log(label(reader));
console.log(label({ id: 2, name: "jo", email: "jo@x.com" }));
type Admin = ApiUser & { level: number };
const boss: Admin = { id: 3, name: "al", level: 2 };
console.log(label(boss) + " level " + boss.level);
const levels: Record<string, number> = { al: 2 };
console.log(levels.al);
console.log(label({ id: 4, name: "zo", nickname: "z" }));

Compiler not loaded. Check or Run loads it here (about 9 MB) — nothing downloads until you do.

Runner helpers

sleep(ms) waits; fakeFetch(url) returns deterministic sample data. TypeScript is stripped before running; imports and exports are not available in the sandbox.

Try removing nickname from the last call — clean. Then add readonly before id and attempt reassignment: the compiler stops you, although JavaScript itself would allow it. That gap (compile-time promise, no runtime lock) surprises people once, then never again.

So interface or type? Interfaces describe object shapes and merge across declarations; type aliases cover everything else (unions, tuples, mapped shapes) and combine with &. For plain objects they are interchangeable — say that, then mention extending versus intersection.

Will it compile? — decide first, then reveal.

check.ts
interface Card {
id: number;
name: string;
}
const card: Card = { id: 1, name: "x", nickname: "y" };
console.log(card);

Interview questions

How do you describe the shape of an object in TypeScript?basic

You write an object literal type naming each property and its type, like { name: string; age: number }. The compiler then requires those exact properties with compatible values. Add ? for optional members and readonly for fields that must not be reassigned. For reused shapes, lift the literal into an interface or type alias so every function shares one named contract instead of repeating the literal.

Interviewers also ask

  • How do you mark a property optional?
What is the difference between interface and type?intermediate

Both name an object shape, and for everyday use they are interchangeable. An interface can be extended with extends and re-opened across declarations, which libraries use to augment shapes. A type alias can additionally name unions, tuples, and mapped shapes that an interface cannot express. As a rule of thumb, use interface for public object contracts and type when you need unions or computed transformations.

Interviewers also ask

  • Which one can name a union like string | number?
How do optional properties work?basic

You add ? after the name, like nickname?: string, which lets callers omit the property entirely. Inside your code the value reads as string | undefined, so you must handle the missing case before using it. Keep optional fields for genuinely absent data, not for values you always set — overusing ? pushes undefined checks onto every reader. Pair optionals with defaults or narrowing at the point of use.

Interviewers also ask

  • What type does an optional property read as?
What does readonly do on a property?basic

It forbids reassignment after creation, so readonly id: string can be set once in the object literal or constructor and never changed again. The compiler rejects later writes, which protects identifiers and shared config. Note it is a compile-time guard only — the emitted JavaScript still allows the write at runtime. For arrays, use readonly T[] or Readonly<T> to get the same one-way protection across the whole shape.

Interviewers also ask

  • Does readonly protect you at runtime?
What are excess property checks?intermediate

When you pass a fresh object literal to a typed position, the compiler rejects unknown extra properties — passing { name, age, nick } where only { name, age } is expected is an error. It catches your typos instead of silently ignoring the extra field. The check only applies to fresh literals; assigning through a variable skips it. If extras are legitimate, add an index signature or extend the target type rather than working around the error.

Interviewers also ask

  • Why does the same object pass through a variable but fail inline?
How do you extend an interface or combine object types?intermediate

You extend an interface with extends, like interface Admin extends User { level: number }, which inherits every member of User plus the new ones. With type aliases you intersect instead: type Admin = User & { level: number }. Both produce one flattened shape the compiler checks as a whole. Prefer extends for clear hierarchies and intersections when you compose small reusable pieces. Conflicting property types in an intersection become never.

Interviewers also ask

  • What happens if two intersected types disagree on a property?
What is structural typing?intermediate

TypeScript checks what an object contains, not what class or interface name it was declared with. If a function wants { name: string }, any object with at least a string name is accepted — extra properties are fine and no explicit implements is needed. That keeps plain object literals and third-party shapes interoperable. The tradeoff is that two unrelated shapes with the same members look identical to the checker, so use branding or literal tags when identity matters.

Interviewers also ask

  • Do you need implements for a plain object to match an interface?
How do index signatures work?intermediate

They describe objects with unknown keys but known value types, like { [key: string]: number } for a score table. You read and write any string key and the compiler enforces the value type. Use them for dictionaries and lookup maps, but prefer explicit properties or Record unions when the keys are known — named fields document intent and catch typos that an open index signature would silently accept.

Interviewers also ask

  • When is Record a better fit than an index signature?

Key takeaways

  • Declare the shape once; every consumer inherits the optionality rules.
  • Excess property checks only bite fresh literals — that is the feature.
  • interface for object shapes, type for everything composed.

References

On this page