
Add Two Numbers in TypeScript: A LeetCode Linked List Solution

The "Add Two Numbers" problem is a well‑known 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, left‑to‑right 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 while 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 → 2564: 4 → 6 → 5The objective is to produce a linked list that represents the resulting sum:
807: 7 → 0 → 8
Solution
To resolve this problem, there are some fairly simple steps to follow:
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 digit‑by‑digit addition.
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.
Manage Carry
:- If the total is 10 or greater, carry
1into the next iteration; otherwise, carry0. - Store only the current digit by taking
sum % 10, and move on to the next node.
- If the total is 10 or greater, carry
Edge Cases
:- If one list is longer, continue the process with this list, always accounting for potential carry‑over.
- If both lists have been completely traversed but there's a remaining carry‑over, ensure it's added as a final node in the result list.
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;};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 starts with the happy path from the worked example: two linked lists of equal length with a straightforward expected result. It then adds a few important edge cases to verify that the function also handles a final carry, different list lengths, zero values, and carries that propagate across multiple nodes.
import { addTwoNumbers } from './addTwoNumbers';describe('Add Two Numbers', () => { it('adds numbers represented as linked lists', () => { const l1 = { val: 2, next: { val: 4, next: { val: 3, next: null } } }; const l2 = { val: 5, next: { val: 6, next: { val: 4, next: null } } }; const result = addTwoNumbers(l1, l2); const expected = { val: 8, next: { val: 0, next: { val: 7, next: null } } }; expect(result).to.deep.equal(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).to.deep.equal(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).to.deep.equal(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).to.deep.equal(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).to.deep.equal(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
This is a more interesting problem than it first appears, because it combines basic arithmetic with linked list traversal and state management. Once you recognise that the digits are stored in reverse order, the solution becomes a straightforward digit‑by‑digit addition with carry.
Related Articles

Trigonometric Functions in CSS. 
LeetCode: Solving the 'Merge Two Sorted Lists' Problem. LeetCode: Solving the 'Merge Two Sorted Lists' Problem

Understanding JavaScript's sort() Method. Understanding JavaScript's
sort()Method
Using Middleware in Next.js for Route Protection. Using Middleware in Next.js for Route Protection

Using CommonJS to Implement Modules in JavaScript. Using CommonJS to Implement Modules in JavaScript

What Does a Software Engineer Do? What Does a Software Engineer Do?

How to Handle Multiple Named Exports in One JavaScript File. How to Handle Multiple Named Exports in One JavaScript File

Understanding the JavaScript Event Loop. Understanding the JavaScript Event Loop

The arguments Object vs. Rest Parameters in JavaScript. The
argumentsObject vs. Rest Parameters in JavaScript
Object.is() vs. Strict Equality in JavaScript. Object.is()vs. Strict Equality in JavaScript
Validating Parentheses Input Using TypeScript. Validating Parentheses Input Using TypeScript

String.startsWith(), endsWith(), and includes() in JavaScript. String.startsWith(),endsWith(), andincludes()in JavaScript