retirement-calculator
The financial math engine that powers Wellgrove.
Retirement Calculator Code
Pure TypeScript, zero runtime dependencies
The Problem

Every time I needed to add financial calculations to a project, I'd either:

  • 1.Find a package that did 80% of what I needed
  • 2.Copy-paste code from StackOverflow and hope the math was right
  • 3.Implement it myself and spend hours testing edge cases

So I built the package I kept wishing existed.

What It Does

retirement-calculator is a TypeScript library for retirement math. No runtime dependencies, no bloat, just math that's been tested to death.

Installation
bash
npm install retirement-calculator
Core Calculations

Goal-Based Retirement Planning

The question that started all of this: how much do I need to save to get where I want to be?

typescript
import { RetirementCalculator } from "retirement-calculator";

const calculator = new RetirementCalculator();

const plan = calculator.getContributionNeededForDesiredBalance(
  50000, // starting balance
  2000000, // goal
  30, // years
  0.07, // 7% annual return
  12, // contribute monthly
  12, // compound monthly
);

console.log(`Save $${plan.contributionNeededPerPeriod.toFixed(2)} a month`);
// Save $1299.15 a month

console.log(
  `With 2% inflation: $${plan.contributionNeededPerPeriodWithInflation.toFixed(2)}`,
);
// With 2% inflation: $2621.57

That second number is the one people forget. $2 million in 30 years buys a lot less than $2 million today, so the package also tells you what it takes to hit the goal in today's dollars.

Compound Interest Projections

typescript
const result = calculator.getCompoundInterestWithAdditionalContributions(
  10000, // starting balance
  500, // monthly contribution
  30, // years
  0.07, // 7% annual return
  12, // contribute monthly
  12, // compound monthly
);

console.log(`You'll have $${result.balance.toFixed(2)}`);
// You'll have $694708.72

console.log(`$${result.totalInterestEarned.toFixed(2)} of that is growth`);
// $504708.72 of that is growth

You get the full period-by-period breakdown too, and aggregateDataByYear rolls it up for charts.

Glidepaths

Target-date funds get safer as you approach retirement, so their returns drop over time. Glidepaths model that:

typescript
const result = calculator.getCompoundInterestWithGlidepath(
  50000, // starting balance
  1000, // monthly contribution
  35, // starting age
  65, // retirement age
  {
    mode: "fixed-return",
    startReturn: 0.09, // 9% at 35
    endReturn: 0.05, // 5% at 65
  },
);

console.log(`You'll have $${result.finalBalance.toFixed(2)}`);
// You'll have $1459224.36

There are four modes:

  • •fixed-return: a straight line from an aggressive return to a conservative one
  • •stepped-return: hold a return, then decline to a final one (the Money Guy Show approach)
  • •allocation-based: blend stocks and bonds like a target-date fund
  • •custom-waypoints: set your own targets by age

Retirement Spending

A few small helpers for the other side of retirement: how much you need to spend a given amount each year (the 4% rule by default), how much a balance lets you withdraw, and what a goal costs after inflation.

typescript
calculator.getDesiredBalanceByYearlySpend(60000);
// 1500000
API Design Philosophy
Results Show Their Work

Every calculation returns more than one number. Compound interest gives you the balance, total contributions, total growth, and every compounding period. Glidepaths give you a month-by-month timeline. If you're building a UI, you can show people why the answer is what it is, not just the answer.

Bad Input Fails Loudly

Negative balances, fractional frequencies, returns below -99%: they all throw a clear error instead of quietly returning NaN or Infinity. A wrong number that looks right is worse than an error.

TypeScript First

Full type definitions included, with docs on every method. Your editor will tell you what each parameter is and what you'll get back. There's also a full API reference.

Version 3.0: Making the Math Honest

Version 3.0 fixed bugs I'm a little embarrassed about:

  • •Contributing more often than interest compounded lost money. Weekly contributions of $100 with monthly compounding deposited $46.15 a year instead of $5,200. Every combination of frequencies now deposits exactly what it should.
  • •The goal calculator could miss the goal. With mismatched frequencies, it landed at 97% of the target. It now solves against the same simulator that does the projections, so if you feed its answer back in, you hit the target to the cent.
  • •The "average return" wasn't a real return. The old formula treated every dollar as if it had been invested on day one, which understated returns, sometimes badly. It's now a true time-weighted return you can compare to the S&P 500. Glidepaths also report a money-weighted return: what your dollars actually earned, since most of the money goes in during the later, lower-return years.

Those were breaking changes, so the changelog explains exactly what changed and how to migrate.

The Math Behind It
Compound Interest

The classic future value formula, with regular contributions:

code
FV = PV(1 + r)^n + PMT × [((1 + r)^n - 1) / r]

Where:
- PV = starting balance
- PMT = contribution each period
- r = rate per period (annual rate / periods per year)
- n = number of periods

The package actually simulates it period by period, which is how it handles contributions and compounding on different schedules (weekly deposits with monthly compounding, for example).

Working Backward

Most calculators run that formula forwards: "here's what you'll have." Solving for the contribution answers the more useful question: "here's what you need to save." Rather than trusting a closed-form shortcut, the package solves against the simulator itself, so the forward and backward answers always agree.

Glidepaths

The glidepath simulation steps monthly, applying that month's return for your age. In the simplest mode, the return declines in a straight line:

code
return = start - (start - end) × (currentAge - startAge) / (retirementAge - startAge)

Simple, but effective for modeling how target-date funds behave.

Testing Strategy

Financial calculations need to be right. No "close enough."

  • •219 tests across the standard calculations, glidepaths, and integration scenarios
  • •Every frequency combination is checked, from yearly to daily
  • •Round trips: the goal calculator's answer, fed back into a projection, has to hit the target
  • •Validation: every bad input has to throw a clear error
  • •CI: lint and tests run on every pull request, and the API docs build and deploy from a workflow

Here's the test that guards against the lost-contributions bug:

typescript
it.each([
  [1, 12],
  [12, 12],
  [52, 12],
  [12, 365],
  // ...and more
])(
  "deposits contributionFrequency contributions a year (contribute %i/yr, compound %i/yr)",
  (contributionFrequency, compoundingFrequency) => {
    const result = calculator.getCompoundInterestWithAdditionalContributions(
      0, // starting balance
      100, // contribution
      10, // years
      0, // no interest, so only deposits count
      contributionFrequency,
      compoundingFrequency,
    );
    expect(result.totalContributions).toBe(100 * contributionFrequency * 10);
  },
);
Real-World Usage

Currently powering:

  • •Wellgrove: the retirement checkup and every calculator on the site
Why Open Source It?

Because financial calculations shouldn't be proprietary. The math is public knowledge. The formulas are in textbooks. Making people reimplement this over and over is wasteful.

Also, open source means:

  • •Anyone can verify the math is correct
  • •Bug reports from real-world usage
  • •Contributions for features I haven't thought of
Future Plans

Planning to add:

  • •Monte Carlo simulation, because real markets don't return a steady 7%
  • •Tax modeling (Roth vs. Traditional)
  • •Social Security estimates
Contributing

Found a bug? Have a feature request? PRs welcome at github.com/introvertedspud/retirement-calculator.

Especially interested in:

  • •Additional test cases
  • •Documentation improvements
  • •Real-world usage examples
  • •Bug reports with reproducible cases
Technical Details

No Runtime Dependencies

  • •Pure TypeScript implementation
  • •Nothing extra pulled into your project

Build

  • •Compiled to JavaScript with TypeScript definitions included
  • •Works in Node.js 18+ and in the browser through any bundler

Quality

  • •100% TypeScript
  • •219 tests with Jest
  • •Semantic versioning, with a changelog that explains breaking changes
  • •Lint and tests in CI on every pull request
The Lesson

Building this package taught me more about financial math than years of reading articles. You don't really understand compound interest until you've implemented it. You don't appreciate the edge cases until you've tested them.

Version 3.0 taught me the second half of that: a calculator that's wrong but looks right is the most dangerous kind.

If you need financial calculations in your project, give it a try:

bash
npm install retirement-calculator

And if you find bugs (there are always bugs), let me know.