We use cookies to enhance your experience on the site
CodeWorlds
Back to collections
Guide13 min read

Visual Types, interactive lessons on TypeScript types

Visual Types offers free interactive lessons on the TypeScript type system. Generics, conditional types, infer, and how to apply them in your own code.

Visual Types, interactive lessons on TypeScript types

Have you ever stared at a complex TypeScript type and felt like you were reading hieroglyphs? Omit<Pick<User, "id" | "name" | "email">, "id"> - what does this actually do? How does data flow through conditional types? How does extends in generics differ from extends in interfaces?

Visual Types is a project by Kit Langton that solves this problem in the most elegant way possible - it shows you how TypeScript types work through interactive visualizations and animations. Instead of reading dry definitions, you watch in real time how the type system processes your code.

What is Visual Types?

Visual Types (available at types.kitlangton.com) is a collection of interactive lessons dedicated to the TypeScript type system. Each lesson combines visual animations with explanations, letting you see - literally - how TypeScript resolves types, performs inference, and applies transformations.

The project was created by Kit Langton - a developer known for his work with effect systems (Effect, Scala ZIO) and creating interactive educational tools. Visual Types is part of his broader philosophy: the best way to learn abstract programming concepts is to visualize them.

Why does type visualization matter?

TypeScript's type system is one of the most expressive among popular programming languages. But this expressiveness comes at a cost - complexity. When you start working with:

  • Generics - parametric types that take other types as arguments
  • Conditional types - T extends U ? X : Y - conditional logic at the type level
  • Mapped types - transformations of object keys and values
  • Template literal types - string manipulation at the type level
  • Infer - type extraction from patterns

...the traditional approach (reading documentation) quickly becomes insufficient. You need to see how these mechanisms work step by step.

How do the lessons work?

Interactive visualizations

Each lesson in Visual Types is not static text with code. It's an interactive animation that:

  1. Shows type flow - you see how TypeScript resolves a type step by step
  2. Visualizes transformations - you watch how mapped types transform an object
  3. Animates inference - you observe how infer extracts types from patterns
  4. Allows experimentation - you can modify parameters and observe the results

Narration and context

The lessons don't throw you into the deep end. Each concept is introduced gradually, with natural language explanations before moving to visualizations. This approach makes even surprising behaviours, such as a conditional type distributing over a union, understandable.

Key topics

The material runs to twenty four lessons arranged in five groups: fundamentals, a second helping of fundamentals, object patterns, conditional types, and utility types. Below are the mechanisms you will see in them.

Type system fundamentals

Visual Types starts with the foundations - how TypeScript understands types, what type narrowing is, how structural typing works. These basics are crucial because all advanced concepts build upon them.

Code
TypeScript
type IsString<T> = T extends string ? true : false

type A = IsString<"hello">  // true
type B = IsString<42>        // false

Instead of guessing what extends does in a type context, Visual Types visually shows how TypeScript compares the type "hello" with string and why the result is true.

Generics

Generics are one of the most important concepts in TypeScript. Visual Types visualizes them as "slots" into which you place concrete types:

Code
TypeScript
type Box<T> = {
  value: T
  isEmpty: boolean
}

type StringBox = Box<string>
// { value: string; isEmpty: boolean }

type NumberBox = Box<number>
// { value: number; isEmpty: boolean }

The visualization shows how T is replaced by a concrete type everywhere it appears - not as an abstract substitution, but as an animated flow.

Conditional types

Conditional types are a powerful tool, but nesting them can be hard to follow:

Code
TypeScript
type TypeName<T> =
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  "object"

type T0 = TypeName<string>   // "string"
type T1 = TypeName<boolean>  // "boolean"
type T2 = TypeName<Date>     // "object"

Visual Types shows the evaluation path - which branch is selected and why, with the active branch highlighted.

Mapped types

Mapped types let you transform object shapes:

Code
TypeScript
type Readonly<T> = {
  readonly [K in keyof T]: T[K]
}

type Optional<T> = {
  [K in keyof T]?: T[K]
}

interface User {
  name: string
  age: number
  email: string
}

type ReadonlyUser = Readonly<User>
type OptionalUser = Optional<User>

The visualization iterates through each object key, showing how K in keyof T goes through "name", "age", "email" and what transformation it applies.

The infer keyword

infer is probably the hardest element to understand in TypeScript. Visual Types makes the biggest difference here - instead of an abstract description, you see how TypeScript "matches" a pattern and extracts a type from it:

Code
TypeScript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never

type Fn = (x: number) => string
type Result = ReturnType<Fn>  // string

The visualization shows how TypeScript matches (x: number) => string to the pattern (...args: any[]) => infer R, "discovering" that R = string.

Distributive conditional types

One of the most surprising behaviors in TypeScript:

Code
TypeScript
type ToArray<T> = T extends any ? T[] : never

type Result = ToArray<string | number>
// string[] | number[]  (not (string | number)[]!)

Visual Types visualizes distribution - how a union type is decomposed into individual elements, each processed separately, and the results combined back into a union.

What the lessons do not cover

Knowing where the material stops is worth as much, since older write ups credit it with more than it actually holds. Among the twenty four lessons there is none dedicated to types built from literal templates or to recursive types, although you will meet both mechanisms in library code:

Code
TypeScript
type EventName<T extends string> = `on${Capitalize<T>}`

type ClickEvent = EventName<"click">     // "onClick"
type HoverEvent = EventName<"hover">     // "onHover"

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K]
}

The material ends on the utility types group, meaning Pick, Parameters, and a function return type. The two mechanisms above are the natural next step once you finish it, but you have to look for them elsewhere.

Comparison with other resources

FeatureVisual TypesTypeScript docsType ChallengesTotal TypeScript
VisualizationsYesNoNoPartially
InteractivityYesNoYes (editor)Yes
AnimationsYesNoNoNo
PriceFreeFreeFreePaid
LevelIntermediate-advancedAllAdvancedAll
FormatVisual lessonsDocumentationChallengesVideo courses

When to choose Visual Types?

  • You understand TypeScript basics, but advanced types are unclear to you
  • You're a visual learner and need to "see" how things work
  • You want to understand how TypeScript resolves types step by step
  • You prefer short, focused lessons instead of lengthy courses

When to reach for something else?

  • TypeScript docs - if you need a complete reference
  • Type Challenges - if you want to practice by solving problems
  • Total TypeScript - if you're looking for a comprehensive course from scratch

Who is Kit Langton?

Kit Langton is a developer and educational tool creator whose life goal is to "precipitate the age of effect systems." He has worked with many programming languages - Haskell, Scala, Swift, TypeScript, Ruby, Rust - and is known for creating interactive visualizations for abstract programming concepts.

Other projects by Kit Langton

  • Effect Institute - interactive lessons for the Effect library
  • Visual Effect - Effect library visualizer
  • Effect Solutions - guide to idiomatic Effect
  • Hex - free macOS speech-to-text app (2813 GitHub stars)
  • A Macro Almanac - a book on metaprogramming in Scala

Visual Types fits into Kit Langton's philosophy - abstract concepts are best taught through interactive visual experiences.

How to get started?

  1. Go to types.kitlangton.com
  2. Choose a lesson that interests you
  3. Click through the visualizations, read the explanations
  4. Experiment with parameter changes
  5. Repeat lessons you don't understand the first time

No installation, no account, no registration. Everything works directly in the browser.

Practical applications of Visual Types knowledge

Knowledge of advanced TypeScript types has a direct impact on everyday work:

Better library APIs

Code
TypeScript
type EventMap = {
  click: MouseEvent
  keydown: KeyboardEvent
  scroll: Event
}

function on<K extends keyof EventMap>(
  event: K,
  handler: (e: EventMap[K]) => void
): void {
  document.addEventListener(event, handler as EventListener)
}

on("click", (e) => {
  console.log(e.clientX)
})

Type-safe builders

Code
TypeScript
type Builder<T extends Record<string, unknown>> = {
  set<K extends keyof T>(key: K, value: T[K]): Builder<T>
  build(): T
}

Type-level validation

Code
TypeScript
type PathParams<Path extends string> =
  Path extends `${string}:${infer Param}/${infer Rest}`
    ? Param | PathParams<Rest>
    : Path extends `${string}:${infer Param}`
    ? Param
    : never

type Params = PathParams<"/users/:id/posts/:postId">
// "id" | "postId"

Where this knowledge actually pays off

It is only fair to say that most application code needs no advanced types. A component taking three props, a function fetching from an API, a form with validation, all of it works with interfaces and simple generics. Conditional and recursive types improve nothing there and hurt readability.

There are three places, though, where this knowledge repays the effort several times over, and knowing them tells you when to reach for the heavier tools.

The first is the boundary between your code and somebody else's, meaning a library's public API or a shared package in a monorepo. There the type is documentation nobody can ignore, and the one place where precision is worth investing in. A function that highlights an error in the editor on misuse rather than failing at runtime saves more time than writing it cost.

The second is working with data whose shape is defined elsewhere: API responses, database schemas, configuration files. Deriving the type from that definition rather than writing it in parallel removes an entire class of bugs caused by two sources of truth drifting apart. That is the real reason libraries like Zod are built around type inference rather than manual declarations.

The third is reading somebody else's code and needing to understand it. Not everyone writes advanced types, but almost everyone uses them by way of libraries. Being able to read a signature from TanStack Query or a database access layer is more useful than being able to write something similar yourself.

Common pitfalls when learning advanced types

The first is believing that because something can be expressed in types, it should be. The type system is computationally complete, so almost anything is expressible, including things nobody after you will decipher. A practical rule: if a type needs a comment explaining how it works, it probably should have been written more simply.

The second is ignoring compilation cost. Recursive types operating on long tuples can stretch project checking from seconds to minutes, and with a sufficiently complex case the compiler simply gives up and reports exceeding its depth limit. That is not a theoretical risk but something real projects hit once the data they describe grows.

The third is learning the syntax without understanding distribution. A conditional type applied to a union does not operate on it as a whole but decomposes into individual members and reassembles the result. Anyone unaware of that gets results that look random and usually works around the problem instead of understanding it.

The fourth is relying on inference where a declaration belongs. The compiler infers types inside a function very well, but at a module boundary an explicit signature is better: it stabilises the interface, speeds up checking, and produces a readable error instead of an inference chain stretched across half the project.

The fifth is learning detached from your own code. Visual material helps you grasp a mechanism, but the knowledge only settles once you apply it to a problem you genuinely have. After each lesson, find a place in your project where that mechanism improves something and make the change.

The sixth and most insidious is confusing a correct type with a correct program. A type describes the shape of data at compile time and says nothing about what arrives over the network at runtime. An API response typed as an object with a string field can still arrive without that field, and the compiler neither knows nor can know. Places where data enters the system from outside need runtime validation, and the type there is a conclusion drawn from that check rather than a substitute for it. There is more on the language itself and on this distinction in the TypeScript writeup.

FAQ

Is Visual Types free?

Yes, the material is available in a browser at no cost and without creating an account. There is no paid tier and no portion of the lessons held back.

Do I need to know TypeScript to start?

Yes, at a basic level: primitive types, interfaces, simple generics. The material begins roughly where a typical introductory course ends, so without that foundation the visualisations will show a mechanism you have nothing to attach to.

How does this differ from Type Challenges?

In scope and purpose. Type Challenges is a set of problems to solve yourself, useful for testing and consolidating knowledge. Visual Types explains how the mechanisms work. In practice they complement each other: understanding first, then practice.

Is this enough to write TypeScript libraries?

Not on its own, since library work also involves API design, version handling, and testing types. It does provide the foundation without which that work is guesswork, namely understanding what the compiler actually does with your declarations.

Is it worth learning if I write ordinary applications?

To a limited extent, yes. Even if you never write a conditional type yourself, you will read library signatures that use them and error messages that refer to them. Being able to decode those shortens the time spent guessing what the compiler wants from you. Keep the proportions in mind: a few hours spent understanding the mechanisms is enough to read somebody else’s code fluently, while writing your own elaborate types demands a far larger investment that repays only on library work.

The material lives at types.kitlangton.com, and the official description of the type system is in the TypeScript documentation.