TypeScript

It's 2 AM! Time to FizzBuzz

An old violin player in a stone arched niche
Image by Frans van Mieris the Elder, Public domain, via Wikimedia Commons

You can do a lot of dumb things with TypeScript, so let's do it! Here comes FizzBuzz!

I did it! I made FizzBuzz in TypeScript! And no, of course we're not using the runtime to calculate it. We're using the type system. This actually turned out to be quite easy, and very Haskell-like. I had to make some type optimization to make the types actually resolve, such as making it tail end recursive. I didn't know the types had this optimization, but I would end up with a stack overflow otherwise, so I guess so.

The idea is very simple:

  • Have a tuple of digits representing the numbers.
  • Use an arbitrary function for incrementing each number, mimicking the arithmetic operation.
  • Create a tuple of all the numbers from 0 to 99.
  • FizzBuzz every multiple of 3, 5, and 15

You can play around with it at the TypeScript Playground. There's a complaint about a possibly infinite type but it's able to resolve it, which is the only thing that matters.

type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type Inc<N extends Digit> = N extends 0
  ? 1
  : N extends 1
  ? 2
  : N extends 2
  ? 3
  : N extends 3
  ? 4
  : N extends 4
  ? 5
  : N extends 5
  ? 6
  : N extends 6
  ? 7
  : N extends 7
  ? 8
  : N extends 8
  ? 9
  : never;

type Next<A extends [Digit, Digit]> = A extends [9, 9]
  ? never
  : A extends [infer X extends Digit, infer Y extends Digit]
  ? Y extends 9
    ? [Inc<X>, 0]
    : [X, Inc<Y>]
  : never;

type DigitsString<A> = A extends [infer X extends Digit, infer Y extends Digit]
  ? `${X extends 0 ? "" : X}${Y}`
  : never;

type PickEvery<
  V extends [Digit, Digit],
  Every extends [Digit, Digit],
  Counter extends [Digit, Digit],
  U
> = Counter extends Every
  ? Next<V> extends never
    ? U
    : PickEvery<Next<V>, Every, [0, 1], V | U>
  : PickEvery<Next<V>, Every, Next<Counter>, U>;

type Multiple<X extends [Digit, Digit]> = PickEvery<X, X, X, never>;

type Threes = Multiple<[0, 3]>;
type Fives = Multiple<[0, 5]>;
type Fifteens = Multiple<[1, 5]>;

type From0To99<A extends [Digit, Digit], Acc extends string[]> = [
  Digit,
  Digit
] extends A
  ? never
  : Next<A> extends never
  ? [...Acc, "Fizz"]
  : A extends Fifteens
  ? From0To99<Next<A>, [...Acc, "FizzBuzz"]>
  : A extends Fives
  ? From0To99<Next<A>, [...Acc, "Buzz"]>
  : A extends Threes
  ? From0To99<Next<A>, [...Acc, "Fizz"]>
  : From0To99<Next<A>, [...Acc, DigitsString<A>]>;

type FizzBuzz = From0To99<[0, 0], []>;