Back to News & Insights
JavaScript August 13, 2026 · 12 min read

TypeScript Intersection Types Done Right: When They Compose Cleanly and When They Silently Lie

TypeScript Intersection Types Done Right: When They Compose Cleanly and When They Silently...

TypeScript Intersection Types Done Right: When They Compose Cleanly and When They Silently Lie

TypeScript Intersection Types Done Right: When They Compose Cleanly and When They Silently Lie

This article was written with the assistance of AI, under human supervision and review.

Most TypeScript composition failures stem from developers treating intersection types as simple object merging. The pattern teams overlook is that intersections follow set-theoretic rules, not object-spread semantics. When developers write A & B, they expect "all properties from A plus all properties from B." What they get is "values that satisfy both A and B simultaneously." This distinction is critical because it determines when composition produces useful types and when it silently creates never, breaking type safety without warning.

The failure mode here is subtle but expensive. A developer combines two types expecting a richer interface. The compiler accepts it. Tests pass. Then production breaks because the intersection resolved to never, accepting literally any value. The fix requires understanding when intersection types compose cleanly versus when they conflict, and knowing which composition tool to reach for in each scenario.

The solution is recognizing that intersection types work for compatible structures. When types share conflicting property signatures, the intersection collapses to never. When they share compatible or non-overlapping properties, they compose cleanly into a richer type that enforces both contracts.

Key Takeaways Intersection types follow set theory: A & B means "values satisfying both A and B," not "merge all properties." Conflicting property signatures (same name, incompatible types) collapse intersections to never, silently breaking type safety. Compatible structures (non-overlapping properties or matching signatures) compose cleanly into enforced combined contracts. Intersections excel at mixing capabilities; unions excel at "one of several shapes" scenarios. The never trap appears when runtime shapes cannot simultaneously satisfy both types, making every value assignable.

Intersection types create a type that must satisfy all constituent types simultaneously. The syntax A & B produces a type where every value must be both an A and a B at the same time. This matters because developers often mistake intersections for object spread or merging, leading to surprising results when types conflict.

The intersection succeeds here because the properties do not conflict. An object can have both id: string and createdAt: Date simultaneously. The compiler enforces both contracts, requiring all properties from both types.

The trouble begins when property signatures conflict. If two types define the same property name with incompatible types, the intersection becomes never because no runtime value can simultaneously satisfy both constraints.

The implication here is that TypeScript resolved status to never because no value is both a number and a string. The type system correctly identified an impossible constraint, but the error message appears at assignment time, not at the intersection declaration. Teams often miss this until runtime behavior breaks.

Understanding this mechanism prevents the most common intersection pitfall: assuming compatibility when types actually conflict. The next section shows when intersections compose cleanly and deliver the intended behavior.

Intersections compose cleanly when types contribute non-overlapping properties or when overlapping properties share identical signatures. This pattern appears frequently in capability mixing, where each type represents a distinct concern that enriches the final interface.

The intersection succeeds because each type contributes unique properties. No conflicts exist, so the compiler enforces all nine properties. This matters because developers can compose rich domain models from smaller, focused types without introducing fragility.

Compatible overlapping properties also compose cleanly. When two types share a property name but the signatures match exactly, the intersection preserves the single property definition.

The id property appears in both source types with identical signatures (string), so the intersection keeps one copy. The compiler does not duplicate properties; it unifies them when compatible. This distinction is critical for understanding when composition succeeds versus when it produces never.

The practical benefit is that teams can build type hierarchies from reusable fragments. Audit trails, timestamps, versioning, and soft-delete patterns compose into complete entity types without manual duplication. The type system enforces every capability, catching missing properties at compile time.

In other words, intersections work perfectly when developers compose compatible, non-conflicting structures. The failure mode emerges when property signatures clash, turning a well-intentioned composition into a silent type-safety trap.

The never trap occurs when intersections resolve to never due to conflicting property signatures, yet the compiler allows assignments that should fail. This matters because never represents the empty type—no value inhabits it—so TypeScript's assignability rules invert: everything becomes assignable to never. The result is silent type-safety loss.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation