Skip to content

Type-Level Hangman in TypeScript

Implementing a Hangman game entirely at the type level using TypeScript’s type system.

Published
Aug 24, 2025
Updated
Aug 24, 2025
Reading time
2 min read

You can actually implement Hangman in TypeScript at the type level. Your guessed letters are represented as a type, and the result is also a type. There’s no runtime code - just pure type manipulation.

A Hangman game implemented in TypeScript at the type level

It may not look exactly like classic Hangman, since TypeScript doesn’t support displaying multi-line template literal types. Instead, we use a single-line emoji visualization.

How many languages let you do this? TypeScript is impressive!

Implementation

And here's the code. Open this in the TypeScript Playground and the ^? will show you the result type directly inline.

type Input = ""; // <-- Enter your guessed letters here

type X = Guess<Input>;
//   ^?

// ------------------------------------------------------------------------

type Guess<T extends string> = `${GameLetters<T>} => ${GameState<T>}`;

type GameLetters<TGuess extends string> = Letters<
  GameSolution,
  LettersOf<Uppercase<TGuess>>
>;

type GameState<TGuess extends string> = Drawing<
  NumberOfWrongGuesses<LettersOf<GameSolution>, Uppercase<TGuess>>
>;

type LettersOf<T extends string> = T extends `${infer TLetter}${infer TRest}`
  ? TLetter | LettersOf<TRest>
  : never;

type Letters<
  TWord extends string,
  TGuessedLetters extends string,
> = TWord extends `${infer TLetter}${infer TRest}`
  ? `${TLetter extends TGuessedLetters ? TLetter : "_"}${Letters<
      TRest,
      TGuessedLetters
    >}`
  : "";

type NumberOfWrongGuesses<
  TWordLetters extends string,
  TGuess extends string,
  TStrikes extends string[] = [],
> = TGuess extends `${infer TLetter}${infer TRest}`
  ? NumberOfWrongGuesses<
      TWordLetters,
      TRest,
      TLetter extends TWordLetters
        ? TStrikes
        : TLetter extends TStrikes[number]
          ? TStrikes
          : [...TStrikes, TLetter]
    >
  : TStrikes["length"];

type Drawing<TIndex extends number> = Drawings[TIndex] extends undefined
  ? LastOf<Drawings>
  : Drawings[TIndex];

type Drawings = [
  "🥳______",
  "🥳_____💣",
  "😐____💣",
  "☹️___💣",
  "😦__💣",
  "😵_💣",
  "😱💣",
  "☠️",
];

type LastOf<T> = T extends [...infer _, infer TLast] ? TLast : never;

type GameSolution = Uppercase<"TypeScript">;