Can you extend types in TypeScript? Diving into performance and optimization
Hello, friend! If you've ever written in TypeScript, you've probably come across the question: "Can you extend types the same way as interfaces?" This question on Stack Overflow has gathered thousands of views, and for good reason. It seems simple: interfaces are extended using extends, and types are extended using intersections (&). But there are nuances, and they directly affect code performance and readability.
Today we'll figure out how to extend types in TypeScript, how it differs from interfaces, and how to avoid pitfalls during optimization. Let's go!
Type vs Interface: The Eternal Battle
Before talking about extension, let's recall the difference. Interfaces are "contracts" that can be declared multiple times (declaration merging), while types are aliases for any shape. Both can be extended, but in different ways.
How to extend interfaces?
This is all familiar:
interface Animal { name: string;}
interface Bear extends Animal { honey: boolean;}
const bear: Bear = { name: 'Winnie', honey: true };Simple, elegant, it works. But what if we want the same for a type?
How to extend types?
Types do not support extends directly. Instead, the intersection operator (&) is used:
type Animal = { name: string;};
type Bear = Animal & { honey: boolean };
const bear: Bear = { name: 'Winnie', honey: true };Looks similar, but there are pitfalls. Let's break them down with examples.
Performance: Which is faster?
The million-dollar question: does choosing interface extends or type & affect compilation and execution speed? Spoiler: yes, but not in the way you think.
Compilation: Interfaces win
TypeScript compiles interfaces faster than type intersections. Why? Interfaces are "flat" structures that the compiler can cache and process linearly. Type intersections are complex constructs that may require recursive expansion.
// Interface - fastinterface A { a: string }interface B extends A { b: number }
// Type - slowertype A = { a: string }type B = A & { b: number }In large projects with hundreds of types, the difference can be noticeable. But if you have 10-20 entities, you won't feel it.
Execution: No difference
TypeScript is a superset of JavaScript. At runtime, there are no interfaces or types. They exist only during development. Therefore, they have no impact on program execution speed at all.
When are types better than interfaces?
Despite the potential compilation slowness, types are indispensable in some scenarios:
- Union types — combining types (e.g.,
string | number) - Conditional types — conditional types (e.g.,
T extends string ? 'yes' : 'no') - Mapped types — transforming properties (e.g.,
{ [K in keyof T]: boolean })
Interfaces cannot handle these. But if you simply need to extend an object — interfaces rule.
Optimization: How to avoid shooting yourself in the foot
Now for the most interesting part. How to write extensible types without slowing down compilation or confusing colleagues?
Rule 1: Use interfaces for objects, types for everything else
This is the golden standard. If you are describing an object's shape — use interface. If you need a union, intersection, or generics magic — use type.
// Goodinterface User { id: number; name: string;}
type Status = 'active' | 'inactive';type Admin = User & { role: 'admin' };<