---
title: "07 — Interfaces: Naming the Shape of Objects"
uid: typescript-interfaces
tags: ["types-vs-interfaces", "interfaces", "typescript", "unknown", "roadmap:typescript", "never"]
excerpt: "An interface is a named contract for object shape that produces no JavaScript — and never/unknown bookend the type system at the bottom and top."
date: 2026-08-13T03:27:28+0000
source: https://www.aveshina.my.id/en/blog/typescript-interfaces
---

"Just another spelling for a type" was my interface read, which hid what makes it special. Writing it down separated the concerns: **an interface is a named contract describing the shape an object must have, it produces no JavaScript, and TypeScript's type system is bounded at the bottom by never and at the top by unknown.** [1] Each of those ideas earns its own attention.

The starting claim is mechanical. An interface declares a set of property names, their types, and whether each is optional. Any object that matches the shape satisfies the interface, no implements keyword required (that's structural typing again). And the interface itself vanishes at compile time — it exists only for the type checker [1].

```
interface User {
  id: number;
  name: string;
  email?: string; // optional
}
```

The ? marks email as optional; everything else is required. Any object with at least id and name of the right types satisfies User, regardless of what else it carries.

## never and unknown: the two ends

Two types bookend the whole system, and understanding them clarified a lot.

**never** is the bottom type — the type of values that *never occur* [2]. A function that always throws or runs forever implicitly returns never. You can't assign anything to never (except never itself). Its practical use is **exhaustive checking**: in a discriminated union with a finite set of cases, assigning the "default" branch to never makes the compiler error if a new case is added but not handled. never is how I make the compiler enforce that every possibility is covered.

**unknown** is the top type — the type of values that *could be anything* [3]. Unlike any, which disables checking, unknown forces me to narrow before use. It's the type-safe way to hold data I don't yet understand: parsed JSON, untyped API responses, values crossing trust boundaries. The hierarchy is never (nothing) at the bottom, concrete types in the middle, unknown (everything) at the top — and any is the unsafe escape hatch that sits beside unknown but skips the narrowing requirement.

## any: the escape hatch

**any** opts a value out of the type system entirely [4]. I can do anything to it, assign it anywhere, and the compiler stays silent. It's occasionally necessary — untyped third-party libraries, rapid prototyping — but every any is a hole where errors hide. The relationship worth remembering: unknown is the *safe* version of "I don't know the type" (it demands narrowing), while any is the *lazy* version (it demands nothing). I reach for unknown and treat any as a deliberate, commented choice.

## Types vs. interfaces

Both declare object shapes, and for most uses they're interchangeable — but the differences are real [5]:

- **Interfaces** describe object and class shapes; they support **declaration merging** (two declarations of the same interface name combine), which makes them the standard for library type definitions and things that need to be extended by consumers.
- **Type aliases** can describe unions, intersections, primitives, and conditional types — shapes interfaces can't express.

The rule I use: reach for an **interface** for plain object and class shapes, especially anything others might need to augment; reach for a **type alias** for unions, intersections, or anything involving computed types. When a shape is purely an object and I'm unsure, the interface is the conservative default because of its extensibility story.

## Extending interfaces

Interfaces can **inherit** from one another, building a new interface on top of an existing one [6]:

```
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }
```

A Dog has both name (from Animal) and breed. This is how I build hierarchies of shapes without repeating fields, and it's the interface analogue of class inheritance. Multiple inheritance is allowed — interface X extends A, B — combining several contracts.

## Declaration and hybrid types

An **interface declaration** is simply the act of introducing one of these named contracts into the type system [6]. Because interfaces can merge, two declarations with the same name in different files combine their members — this is how libraries let consumers augment their types (more on that in the modules notes).

**Hybrid types** describe objects that act as both an object (with properties) and a function (callable). They arise when a function needs to carry associated state — a function with attached properties. They're uncommon but the right tool when a callable thing also needs metadata hanging off it.

## How I use this

My defaults: reach for an **interface** for object/class shapes I expect to extend or that others might augment; reach for a **type alias** for unions, intersections, and computed types; treat **never** as my exhaustive-check enforcer in discriminated unions; treat **unknown** as the safe holder for untrusted data; and treat **any** as a last resort I'd rather justify than default to. The mental shift that stuck: interfaces and aliases aren't competing spellings — they're tools with different strengths, and never/unknown aren't exotic edge cases but the load-bearing ends of the whole type system.

## References

[1] Microsoft, "Object Types," TypeScript Handbook, 2024. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/2/objects.html](https://www.typescriptlang.org/docs/handbook/2/objects.html)

[2] Microsoft, "The never type," TypeScript Handbook, 2024. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/2/narrowing.html#the-never-type](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#the-never-type)

[3] Microsoft, "New unknown top type," TypeScript 3.0 Release Notes, 2018. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type)

[4] Microsoft, "any," TypeScript Handbook, 2024. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any)

[5] Microsoft, "Differences Between Type Aliases and Interfaces," TypeScript Handbook, 2024. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces)

[6] Microsoft, "Extending Interfaces," TypeScript Handbook, 2024. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/2/objects.html](https://www.typescriptlang.org/docs/handbook/2/objects.html)

```quiz
Q: An interface declared in a TypeScript file produces what JavaScript at runtime?
- A class or object constructor
- Nothing — interfaces are erased and produce no JavaScript
correct: 1
explain: Interfaces are purely a compile-time construct. They exist only for the type checker and emit no code, so the runtime never sees them.

Q: A function that always throws an error implicitly returns which type?
- void
- never
correct: 1
explain: never is the type of values that never occur. A function that always throws or loops forever returns never. It's also used for exhaustive checking in discriminated unions.

Q: What is the key difference between `unknown` and `any`?
- unknown is faster
- unknown forces you to narrow before use; any disables checking entirely
correct: 1
explain: Both represent "could be anything," but unknown demands narrowing (typeof, instanceof, assertion) while any skips it. unknown is the type-safe top type.

Q: Which supports declaration merging — two declarations of the same name combining their members?
- interfaces
- type aliases
correct: 0
explain: Interfaces merge when declared multiple times with the same name. This is why they're standard for library type definitions that consumers need to augment. Type aliases cannot merge.

Q: You need a type that is a union of "GET" | "POST" | string literals. Reach for…
- an interface
- a type alias
correct: 1
explain: Type aliases can express unions, intersections, and computed types — shapes interfaces can't. A union of string literals is a canonical type-alias use case.
```
