# TypeScript's Dark Arts: Building a Type-Level Calculator

# Introduction

Have you ever wondered if we could do math without runtime? 😱

Yeah... we're gonna build a little calculator in TypeScript. It's gonna be fun hehe, a bit of a brain teaser. 😆

We’ll do addition, subtraction, division and multiplication.

# The Basics: Types as Values

First, let's start with something surprising: TypeScript's type system is [Turing complete](https://gist.github.com/hediet/63f4844acf5ac330804801084f87a6d4). This means we can do calculations using just types.

```typescript
type IsTwo<T> = T extends 2 ? true : false;
type Result = IsTwo<2>; // true
```

Cute, but boring. Let's get wild.

![Naruto Smile by Narutoria on DeviantArt](https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/c4a4f07e-2f1b-4fc1-a7e5-fa850465d921/d64zfs5-939ce863-6319-49fd-85fb-fd57cdab0503.jpg?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcL2M0YTRmMDdlLTJmMWItNGZjMS1hN2U1LWZhODUwNDY1ZDkyMVwvZDY0emZzNS05MzljZTg2My02MzE5LTQ5ZmQtODVmYi1mZDU3Y2RhYjA1MDMuanBnIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.glofMWdtAJlF8QoZoP4XZooKW0_W8PKWrfxRmXxT4ag align="left")

# Building Numbers From Scratch

In type-land, we can represent numbers as tuples of a specific length.

Why? Because we can measure them!

```typescript
type BuildTuple<
  Length extends number,
  Result extends Array<unknown> = []
> = Result["length"] extends Length
  ? Result
  : BuildTuple<Length, [...Result, unknown]>;

// BuildTuple<3> = [unknown, unknown, unknown]
// Basically: Give me an array of 'Length' unknown elements
// BuildTuple<5>["length"] = 5 😁
```

"But that's just an array!" I hear you say. Yes, but it's an array we can count. And if we can count, we can do math! 😝

# Subtraction

Here's where it gets spicy. Want to subtract 3 from 5? Easy! Just:

1. Build a tuple of length 5
    
2. Try to match it against a pattern that has a tuple of length 3
    
3. Count what's left (by inferring the rest of the tuple)
    

```typescript
type Subtract<
  Value extends number,
  SubtractAmount extends number
> = BuildTuple<Value> extends [...BuildTuple<SubtractAmount>, ...infer Rest]
  ? Rest["length"]
  : never;

type ThreeMinusTwo = Subtract<3, 2>; // 1
```

When we do `Subtract<5, 3>`, here's the galaxy-brain moment of what happens:

```javascript
// 1. BuildTuple<5> creates [unknown, unknown, unknown, unknown, unknown]
// 2. BuildTuple<3> creates [unknown, unknown, unknown]
// 3. Pattern matching:
//    [unknown, unknown, unknown, unknown, unknown] extends
//    [...[unknown, unknown, unknown], ...infer Rest]
// 4. Rest captures [unknown, unknown]
// 5. Rest['length'] = 2 😍
```

# Addition

Take both numbers, build tuples of the appropriate lengths, and spread them together.

```typescript
type Add<A extends number, B extends number> = Length
  [...BuildTuple<A>, ...BuildTuple<B>]
>

type AddTwoAndThree = Add<2, 3>; // [unknown, unknown, unknown, unknown, unknown]
AddTwoAndThree["length"]; // 5
```

# Multiplication

```typescript
type Multiply<
  A extends number,
  B extends number,
  Result extends Array<unknown> = []
> = B extends 0
  ? Length<Result>
  : Multiply<A, Subtract<B, 1>, [...Result, ...BuildTuple<A>]>
```

Multiplication is a bit tricky to understand at first. Let's break it down:

```typescript
type MultiplyTwoByThree = Multiply<2, 3>; // 6
```

It's `MultiplyTwoByThree` is 6 here and we don't need to use `["length"]` because we're returning `Length<Result>`.

The idea is we keep spreading in the tuple of `A` until we've done it `B` times. If `B` extends `0`, it means `B` is `0` therefore we return the length of the result.

Let's go over two iterations of `MultiplyTwoByThree`. First iteration:

```typescript
type Multiply<
  2,
  3,
  []
  // 3 extends 0 is false!
> = 3 extends 0
  ? Length<[]>
  // Build a tuple of 2 elements
  // Spread it into the result along with the empty array (initial results)
  // `B` is now 2 with Subtract<3, 1>
  : Multiply<2, Subtract<3, 1>, [...[], ...BuildTuple<2>]>
```

Second iteration:

```typescript
type Multiply<
  2,
  2,
  [unknown, unknown]
  // 2 extends 0 is false!
> = 2 extends 0
  ? Length<[unknown, unknown]>
  // Build a tuple of 2 elements
  // Spread it into the result along with [unknown, unknown]
  // `B` is now 1 with Subtract<2, 1>
  : Multiply<2, Subtract<2, 1>, [...[unknown, unknown], ...BuildTuple<2>]>
```

# Division

Division is a bit different. Multiplication was repeated addition. Division is repeated subtraction.

```typescript
type Divide<
  A extends number,
  B extends number,
  Count extends Array<unknown> = []
> = A extends 0
  ? Length<Count>
  : // This is never if `A` is less than `B`
  Subtract<A, B> extends never
  ? Length<Count>
  : Divide<Subtract<A, B>, B, [...Count, unknown]>;
```

How it works is Count keeps increasing the number of times we've subtracted `B` from `A` until `A` is less than `B`.

Imagine we have 6 / 2. What you do is you decrement 6 by 2, and every time you do that, you increment a counter. We know it's three, but let's go over it together:

1. 6 - 2 = 4
    
2. 4 - 2 = 2
    
3. 2 - 2 = 0
    

In the fourth iteration, `A` is now 0. We return the length of the count, which is 3.

```typescript
type DivideSixByTwo = Divide<6, 2>; // 3
```

Let's go over this one together. Two iterations like we did before:

```typescript
type Divide<
  6,
  2,
  []
> = 6 extends 0
  ? Length<[]>
  : Subtract<6, 2> extends never
  ? Length<[]>
  : Divide<Subtract<6, 2>, 2, [...[], unknown]>
```

Second iteration:

```typescript
type Divide<
  4,
  2,
  [unknown]
> = 4 extends 0
  ? Length<[unknown]>
  : Subtract<4, 2> extends never
  ? Length<[unknown]>
  : Divide<Subtract<4, 2>, 2, [...[unknown], unknown]>
```

# Utility Types

Some utility types that can come in handy:

```typescript
type Length<T extends Array<unknown>> = T["length"];

type First<T extends Array<unknown>> = T extends [infer First, ...unknown[]]
  ? First
  : never;

type Last<T extends Array<unknown>> = T extends [...infer _, infer Last]
  ? Last
  : never;

// You can do any number!
type Zero = 0;
type One = 1;
type Two = 2;
type Three = 3;
type Four = 4;
type Five = 5;
type Six = 6;
type Seven = 7;
type Eight = 8;
```

# Will I ever need this?

No. Most of the times, if dealing with complicated TypeScript types, it's abstracted away from you by e.g. a library.

However, when building larger systems, you may build your own utilities and abstractions, so knowing TypeScript at a deeper level can be helpful, but not necessary!

# The Real Magic

The real magic isn't in what these types do. It's in realizing that TypeScript's type system is like its own programming language. We're not just defining types. We're creating programs that work during compilation.

Our `BuildTuple` type works like a recursive function. Our `Subtract` type uses pattern matching. We're doing functional programming, but with types!
