TypeScript: How to Turn Union Type to Intersection Type

Updated: January 8, 2024 By: Guest Contributor Post a comment

TypeScript’s nuanced type system offers both union and intersection types. This tutorial demystifies transforming the broad flexibility of unions into the stringent specificity of intersections, lighting the path from versatility to precision.

Introduction

Among software development’s many dark woods, one stands formidable: TypeScript’s type system, a land of bewildering constructs, much like Mississippi’s densest thickets. Here, two curious creatures roam – the nimble Union and the stern Intersection. In the parlance of our type, Union represents a value that could be one thing or another, whilst Intersection, a more demanding beast, insists it be all things at once. Converting a Union to an Intersection type is much like distilling a potent spirit from various milder liquors, a task that, while arcane, grants the craftsman far mightier control over their alchemical types.

Let us commence this journey of elucidation by setting forth with simple examples, gradually venturing into more tortuous territories that would daunt even the most seasoned riverboat captains of the Mississippi.

Basic Transformation

type Cat = {meow: true};
type Dog = {bark: true};
type PetUnion = Cat | Dog;

type Intersection = (Type extends any ? (x: Type) => void : never) extends (x: infer R) => void ? R : never;

type PetIntersection = Intersection<PetUnion>;

// Now PetIntersection is {meow: true} & {bark: true}

This incantation, once wrought, fuses our tranquil cat with our vigilant dog, creating a PetIntersection both meowing and barking in harmonious concert.

Moderate Complexity

interface MailCarrier {
  deliverMail: () => void;
}
interface Programmer {
  writeCode: () => void;
}

type WorkerUnion = MailCarrier | Programmer;

type WorkerIntersection = Intersection<WorkerUnion>;

// Expectation: WorkerIntersection has both deliverMail and writeCode methods

Here, akin to combining professions, we coerce a Postal servant to pair with a Code smith, bestowing upon them double duty in a fanciful melee of mail and manuscripts.

Expanding Brevity

When types encompass generics or complex structures, the transformation apparatus becomes more intricate, akin to navigating the channels of the great Mississippi at night. Behold as we manipulate the bounteous:

type Id<T> = T;
type StringOrNumber = Id<string> | Id<number>;

type StringAndNumber = Intersection<StringOrNumber>;

// Passage through the tempest: StringAndNumber is string & number. But, ha! An impossibility in TypeScript's sober reality.

In this realm of type alchemy, we seek harmonies even amid inherent discordance. Yet, some mixtures, like spirits and steamboat engines, might reveal themselves too volatile to bind.

Advanced Shape-Shifting

type Individual = {name: string};
type Employee = Individual & {employeeId: number};

type PersonUnion = Individual | Employee;

type PersonIntersect = Intersection<PersonUnion>;

// A conjuration rendering PersonIntersect with properties of both Individual and Employee

In this lofty endeavor, type morphing achieves its pinnacle, akin to crafting a steamboat that doubles as a locomotive, a profound articulation of TypeScript’s polymorphic prowess.

Handling Impossible Intersections

In TypeScript, as in life on the Mississippi, some voyages are fated never to bear fruit – such as when types are mutually exclusive:

type Water = {drink: true};
type Fire = {burn: true};
type ElementalUnion = Water | Fire;

type ElementalIntersection = Intersection<ElementalUnion>;

// The output is never, for water quenches fire – and so, the types cannot coexist.

Here, where the natural order prevails, an Intersection is but a mirage upon the water’s surface, for not all unions are meant to become intersections.

Tooling and TypeScript Versions

Before setting sail, ensure your TypeScript is current, for notions of type may evolve like the river’s own ever-shifting bed, and your tooling is aptly provisioned for these transformations.

Conclusion

In conclusion, TypeScript’s trick of turning unions into intersections is a piece of powerful type sorcery. It opens a gateway of possibilities like rivers converging into a powerful force. Equip yourself with patience and practice, and you might navigate these type complexities as deftly as the captains who chart the rolling currents of the Mississippi.