· By John Kavanagh

Add Two Numbers in TypeScript: A LeetCode Linked List Solution

Abstract image used to represent Add Two Numbers in TypeScript
Image by GVZ 42.

The "Add Two Numbers" problem is a wellknown coding interview challenge, best known from LeetCode. The problem may seem deceptively simple at first, but it elegantly combines basic arithmetic with data structure manipulation in this case specifically linked lists.

Let's dive into the problem's details and explore a solution.


Problem Description

Imagine being tasked with adding two numbers together. Simple, right? But with this problem, there's a twist. Instead of presenting numbers in their typical, lefttoright numerical format, each number is represented using a linked list where every digit is a node. Crucially, these numbers are depicted in reverse order. That reverse ordering is what makes the problem manageable, because it lets us start from the least significant digit and work forwards through both linked lists whilst carrying any overflow as we go.

This twist means that the head of the linked list is the least significant digit, and as you traverse the list, you move to increasingly significant digits.

An Example

Consider the numbers 243 and 564. Written traditionally the sum would look something like this:

  2 4 3  
+ 5 6 4  
---------
  8 0 7  

In this problem, however, the digits are stored in reverse order as linked lists:

243: 3 → 4 → 2
564: 4 → 6 → 5

The objective is to produce a linked list that represents the resulting sum:

807: 7 → 0 → 8
A simple black-and-white diagram that demonstrates the input and expected output of the Add Two Numbers problem. These are each number, inside a circle - in the same format as the proceeding code example.

Solution

To resolve this problem, there are some fairly simple steps to follow:

  1. Preparation

    :
    • Prepare a dummy node as the fixed starting point for the result list, so new result nodes can be appended without needing special handling for the first digit.
    • Set an initial carry value of 0, ready to store any overflow from each digitbydigit addition.
  2. Simultaneous Traversal

    :
    • Progress through both linked lists at the same time, one node at a time.
    • At each step, add the current digit from each list, along with any carry from the previous iteration. If one list has already ended, treat its value as 0.
  3. Manage Carry

    :
    • If the total is 10 or greater, carry 1 into the next iteration; otherwise, carry 0.
    • Store only the current digit by taking sum % 10, and move on to the next node.
  4. Edge Cases

    :
    • If one list is longer, continue the process with this list, always accounting for potential carryover.
    • If both lists have been completely traversed but there's a remaining carryover, ensure it's added as a final node in the result list.
  5. Compile the Result

    :
    • Using the dummy node as a reference point, extract the final summed list to return as the result.

Implementing This in TypeScript

A straightforward solution to this problem using TypeScript looks something like this:

type ListNode = {
  val: number;
  next: ListNode | null;
};

export const addTwoNumbers = (
  l1: ListNode | null,
  l2: ListNode | null
): ListNode | null => {
  const dummy: ListNode = { val: 0, next: null };
  let current = dummy;
  let carry = 0;

  while (l1 !== null || l2 !== null) {
    let sum = carry;

    if (l1 !== null) {
      sum += l1.val;
      l1 = l1.next;
    }

    if (l2 !== null) {
      sum += l2.val;
      l2 = l2.next;
    }

    carry = Math.floor(sum / 10);
    current.next = { val: sum % 10, next: null };
    current = current.next;
  }

  if (carry > 0) {
    current.next = { val: carry, next: null };
  }

  return dummy.next;
};

Adding Tests

As always, it is well worth adding some simple test coverage to your function, especially if this is part of an interview or technical test.

The test suite below uses Jest in a project configured to run TypeScript tests. Save the implementation as addTwoNumbers.ts and the tests alongside it as addTwoNumbers.test.ts. The first case follows the worked example: 3 4 2 and 4 6 5 represent 243 and 564, so the result is 7 0 8, representing 807. The remaining cases cover a final carry, different list lengths, zero values and carries across several nodes.

import { addTwoNumbers } from './addTwoNumbers';

describe('Add Two Numbers', () => {
  it('adds numbers represented as linked lists', () => {
    const l1 = { val: 3, next: { val: 4, next: { val: 2, next: null } } };
    const l2 = { val: 4, next: { val: 6, next: { val: 5, next: null } } };

    const result = addTwoNumbers(l1, l2);

    const expected = { val: 7, next: { val: 0, next: { val: 8, next: null } } };

    expect(result).toEqual(expected);
  });

  it('handles a final carry', () => {
    const l1 = { val: 9, next: null };
    const l2 = { val: 1, next: null };

    const result = addTwoNumbers(l1, l2);

    const expected = { val: 0, next: { val: 1, next: null } };

    expect(result).toEqual(expected);
  });

  it('handles lists of different lengths', () => {
    const l1 = { val: 2, next: { val: 4, next: { val: 3, next: null } } };
    const l2 = { val: 5, next: { val: 6, next: null } };

    const result = addTwoNumbers(l1, l2);

    const expected = { val: 7, next: { val: 0, next: { val: 4, next: null } } };

    expect(result).toEqual(expected);
  });

  it('handles zero values', () => {
    const l1 = { val: 0, next: null };
    const l2 = { val: 0, next: null };

    const result = addTwoNumbers(l1, l2);

    const expected = { val: 0, next: null };

    expect(result).toEqual(expected);
  });

  it('handles carry across multiple nodes', () => {
    const l1 = { val: 9, next: { val: 9, next: { val: 9, next: null } } };
    const l2 = { val: 1, next: null };

    const result = addTwoNumbers(l1, l2);

    const expected = {
      val: 0,
      next: {
        val: 0,
        next: {
          val: 0,
          next: { val: 1, next: null },
        },
      },
    };

    expect(result).toEqual(expected);
  });
});

These extra cases matter because they exercise the exact conditions that make this problem more than a simple addition exercise. They confirm that the function does not drop a trailing carry, can continue when one linked list ends before the other, and correctly propagates carry values through multiple iterations.


Wrapping‑up

Walk both linked lists together, add the two current digits and the carry, then store sum % 10 in the result node and carry Math.floor(sum / 10) forward. A dummy head keeps the list construction simple, including the final carry. The representation is reversed; the arithmetic does not need to be.


Want to find out more?

If you need senior handson support with a complex React or Next.js platform, migration, performance issue, or technical SEO problem, send me the context and I'll tell you where I can help.