TypeScript

TypeScript Types as Sets: Subsets, Supersets, Unions, and Intersections – How to Think About TS

One of the most effective ways to understand TypeScript’s type system is to think of types not as rigid rules, but as sets of possible values. This mental model makes concepts like union (|) and intersection (&) much easier to grasp, especially when working with both primitive types and object types.


Types as Sets of Values

In TypeScript, every type can be thought of as a set of possible values.

  • string → the set of all string values: "hello", "world", "", "123", …
  • number → the set of all numbers: 1, 0, -5, 3.14, …
  • boolean{ true, false }

Even any and never fit this mental model:

  • any is the universal set (all possible values).
  • never is the empty set (no possible values).

Subsets and Supersets of Types

Just like in mathematics, some types are subsets of others.

Example with literals:

type Yes = "yes";
type YesOrNo = "yes" | "no";
  • "yes" is a subset of "yes" | "no".
  • "yes" | "no" is a superset of "yes".

👉 You can assign a subset to a superset, but not vice versa:

let a: YesOrNo = "yes"; // ✅ OK
let b: Yes = "yes";     // ✅ OK
// b = "no";           // ❌ Error: "no" not assignable to "yes"

Union Types (X | Y) as Union of Sets

The union operator (|) combines two sets of values into one bigger set.

Example with primitives:

type StringOrNumber = string | number;

This is the union of all strings and all numbers.
So valid values are: "hello", 42, "world", 3.14, …

👉 You can visualize it as:

string = { "a", "b", "c", ... }
number = { 1, 2, 3, ... }
string | number = { "a", "b", "c", ... } ∪ { 1, 2, 3, ... }

Example with objects:

type Cat = { meow: () => void };
type Dog = { bark: () => void };

type CatOrDog = Cat | Dog;
  • A CatOrDog can be a cat, or a dog.
  • If you only know it’s a CatOrDog, you can’t assume both methods exist — you must narrow first.
function speak(pet: CatOrDog) {
  if ("meow" in pet) pet.meow(); 
  if ("bark" in pet) pet.bark();
}

Intersection Types (X & Y) as Intersection of Sets

The intersection operator (&) represents the overlap of sets — values that belong to both types at the same time.

Example with primitives:

type A = string;
type B = number;

type Impossible = A & B; // ❌ never

Since no value can be both a string and a number at once, the result is never (the empty set).

Example with objects:

type Person = { name: string };
type Worker = { company: string };

type Employee = Person & Worker;

Here, the intersection type means:

Employee = Person ∩ Worker = { name: string, company: string }

So an Employee must have both properties:

let e: Employee = { name: "Alice", company: "TechCorp" }; // ✅

Visualizing Union and Intersection

Think of Venn diagrams:

  • Union (|) = everything in circle X plus everything in circle Y.
  • Intersection (&) = only the overlapping part of circle X and Y.

Practical Example: Combining Concepts

type ID = number | string;          // union
type User = { id: ID; name: string };
type Admin = { id: ID; role: "admin" };

type AdminUser = User & Admin;      // intersection
  • ID is a union of strings and numbers.
  • AdminUser is an intersection of User and Admin.

So a valid AdminUser must contain all properties:

let admin: AdminUser = {
  id: "123",
  name: "Alice",
  role: "admin",
};

Conclusion

Thinking about TypeScript types as sets gives you a powerful framework:

  • Every type = set of possible values.
  • Subsets and supersets explain assignability.
  • Union (X | Y) = combination of sets.
  • Intersection (X & Y) = overlap of sets.
  • never = empty set, any = universal set.

With this mental model, TypeScript’s type system becomes much less mysterious and much more intuitive — a natural extension of set theory applied to programming.


Related posts
TypeScript

Literals in TypeScript: String, Number, Boolean, Template, and Compound Literals

Literals are one of the fundamental building blocks of TypeScript programming. They represent fixed…
Read more
TypeScript

Utility Types in TypeScript: Pick, Omit, Partial, Required, Readonly, Record...

✅ — Utility Types are one of the most powerful features of TypeScript, but also one of the most…
Read more
TypeScript

Complete TypeScript Tutorial Online: Master TypeScript in 2025

TypeScript has revolutionized modern web development by bringing static typing to JavaScript, making…
Read more