Extending Interfaces and Intersecting Types
As a codebase grows, types that once felt self-contained start sharing properties. Copying those properties into every type that needs them is the wrong answer — it creates drift when one copy changes and the others do not. TypeScript gives you two composition tools: extends for interfaces, and & for type aliases.
Extending interfaces
Section titled “Extending interfaces”An interface can extend another interface, inheriting all of its properties:
interface BaseQuestion { question: string; category: string; difficulty: Difficulty;}
interface TriviaQuestion extends BaseQuestion { correctAnswer: string; allAnswers: string[];}TriviaQuestion now has five properties: the three from BaseQuestion plus its own two. A value of type TriviaQuestion must satisfy all five:
const q: TriviaQuestion = { question: 'What planet is closest to the Sun?', category: 'Science & Nature', difficulty: 'easy', correctAnswer: 'Mercury', allAnswers: ['Mercury', 'Venus', 'Mars', 'Earth'],};Extending multiple interfaces
Section titled “Extending multiple interfaces”An interface can extend more than one parent at once:
interface WithId { id: string;}
interface WithTimestamp { createdAt: string;}
interface StoredQuestion extends WithId, WithTimestamp { question: string; correctAnswer: string;}StoredQuestion inherits id and createdAt alongside its own properties. This pattern lets you build small, focused interfaces for cross-cutting concerns — identifiers, timestamps, metadata — and mix them into the types that need them.
Overriding inherited properties
Section titled “Overriding inherited properties”An extending interface can narrow (but not widen) an inherited property:
interface BaseQuestion { difficulty: string;}
interface TriviaQuestion extends BaseQuestion { difficulty: Difficulty; // ✓ — 'easy' | 'medium' | 'hard' is narrower than string}Widening an inherited property is an error:
interface Specific { difficulty: Difficulty;}
interface Loose extends Specific { difficulty: string; // Error — string is wider than Difficulty}Intersecting type aliases
Section titled “Intersecting type aliases”Type aliases use & to compose, which you saw in Module 02. The result is a type that requires all properties from all operands:
type WithId = { id: string };type WithScore = { score: number; total: number };
type ScoredEntry = WithId & WithScore;// equivalent to: { id: string; score: number; total: number }& and extends produce structurally equivalent results for simple object shapes. The difference is syntax and intent: extends reads as “is a kind of”, & reads as “has everything from both”.
When to use extends vs &
Section titled “When to use extends vs &”For object types:
- Use
extendswithinterfacewhen the relationship is conceptual inheritance — aTriviaQuestionis aBaseQuestionwith more detail. - Use
&withtypewhen you are mechanically combining two independent shapes — a score record that also needs a timestamp.
In practice, both work. The preference in most TypeScript codebases is interface extends for named data models and & for ad-hoc combinations.
AceIt: RawQuestion and TriviaQuestion share a base
Section titled “AceIt: RawQuestion and TriviaQuestion share a base”RawQuestion (the API shape) and TriviaQuestion (the app shape) share three fields: question, category, and difficulty. You can factor those into a shared base:
interface BaseQuestion { question: string; category: string; difficulty: Difficulty;}
interface RawQuestion extends BaseQuestion { type: string; correct_answer: string; incorrect_answers: string[];}
interface TriviaQuestion extends BaseQuestion { readonly correctAnswer: string; readonly allAnswers: string[];}Both interfaces now document explicitly that they describe the same underlying concept. If a field shared between them changes — say, difficulty becomes a more complex type in a future API version — you change BaseQuestion once.
Exercise
Section titled “Exercise”In types.ts:
- Extract a
BaseQuestioninterface withquestion: string,category: string, anddifficulty: Difficulty. - Rewrite
RawQuestionto extendBaseQuestion, adding only the fields unique to the API shape. - Rewrite
TriviaQuestionto extendBaseQuestion, addingreadonly correctAnswerandreadonly allAnswers. - Create a
type TimestampedHighScore = HighScore & { recordedAt: number }using intersection. Confirm a variable of that type requires all four fields.
interface B extends Ainherits all of A’s properties into B.- An interface can extend multiple parents:
interface C extends A, B. - Extending interfaces can narrow inherited properties but not widen them.
- Type alias intersection (
&) produces a structurally equivalent result for object shapes. - Use
extendsfor conceptual inheritance; use&for mechanical combination of independent shapes.