Intersection Types

September 26, 20254 min read
Requirements:
FunctionsObjects| Unions

Intersection Types in TypeScript: A Beginner Friendly Deep Dive

If you have ever stared at the & operator in TypeScript and wondered if it was some secret math trick, you are not alone. Many beginners confuse intersections with unions and walk away puzzled. This guide is here to clear that fog.

Why Intersection Types Matter

In everyday coding, you often need something to fulfill multiple roles. Imagine hiring someone who can both drive a delivery truck and cook in the kitchen. Instead of writing two separate job descriptions over and over, you can combine them. That is what intersection types do for your code. They let you compose features, avoid duplication, and enforce structure. Your codebase gets cleaner and safer, and you spend less time rewriting the same thing.

The Basics: What Are Intersection Types

An intersection type is the logical AND of types. A value must satisfy all included types at once. The ampersand symbol & is the syntax.

type A = { a: number }
type B = { b: string }
type AandB = A & B
 
const obj: AandB = { a: 42, b: 'hello' }

Here obj must contain both a and b. Think of it as wearing two hats at once. This is very different from a union (|) where you can pick one hat or the other.

Intersection vs Union: AND vs OR

A union type lets a value be one of several options. An intersection type demands all conditions.

type U = { id: number } | { name: string }
type I = { id: number } & { name: string }

Use unions for flexibility. Use intersections for enforcing structure.

Composing Object Types

One common pattern is merging smaller interfaces into richer models.

interface User {
  id: number
  username: string
}
 
interface Profile {
  bio: string
  avatar: string
}
 
type UserProfile = User & Profile

Now UserProfile has both identity and profile info without repeating definitions. This keeps your types DRY and consistent.

Enforcing Multiple Constraints

Sometimes a function parameter needs to be more than one thing at once.

interface Logger {
  log: (message: string) => void
}
interface Identified {
  id: number
}
 
function logUser(user: Logger & Identified) {
  user.log(`User ${user.id} logged in`)
}

The function only accepts objects that both log and have an id. This guarantees correct behavior before the code even runs.

Props and Configurations

In UI frameworks, you often combine props from different sources. Instead of writing a giant interface, intersect smaller ones.

type OwnProps = { id: string }
type ReduxProps = { dispatch: () => void }
 
type Props = OwnProps & ReduxProps

This makes the final type clear and maintainable. The same trick works for configuration objects where you want some fields flexible but others required, such as enforcing a url in a config while keeping the rest optional.

Quirks and Pitfalls

A quick debugging tip: if the compiler complains about never, check if your intersection has incompatible overlapping fields.

Takeaway

Intersection types give you a way to say “this thing must have everything.” They are perfect for merging objects, enforcing constraints, and structuring complex systems. Use them when you need roles combined. Stick to unions when you want either or. With practice, intersections become a natural and powerful tool in your TypeScript toolbox.

Want to test this out? Try refactoring one of your interfaces by combining smaller ones with &. Then hover over the type in your editor to see the merged result. If you hit a weird never error, you now know why.

Share this article

Stay Updated

Get the latest TypeScript tips, tutorials, and course updates delivered straight to your inbox.

No spam, unsubscribe at any time.