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.
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">;