← thecodex.expert · The Codex Family of Knowledge
Tier 0 · Absolute Beginner · TypeScript Project

Temperature Converter

Convert temperatures between Celsius and Fahrenheit. You will learn how a program takes a number, applies a formula, and gives back a result — the essence of almost every useful tool.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want a small program that converts a temperature from Celsius to Fahrenheit, or the other way around. The user picks which direction, types a number, and the program does the maths and shows the answer. Simple — but it teaches the core shape of every calculator-style tool: ask, calculate, show.

Where this shows up in real life: every app that takes a number and transforms it — a currency converter, a tip calculator, a unit converter, a tax estimator — is doing exactly this. Take input, apply a formula, return a result. Master the pattern here and you can build any of them.

2 How to Think About It

Before any code, think about the two things the program needs to know and the two formulas it might use. You do not need TypeScript yet — just the plan:

The plan — in plain English
1. Ask the user which way to convert (Celsius→Fahrenheit, or the reverse). → 2. Ask for the temperature number. → 3. Apply the right formula (C→F is ×9/5 then +32; F→C is −32 then ×5/9). → 4. Show the answer. The only “hard” part is remembering the two formulas — and the program remembers them for you.

1

2

Ask: convert which way?

Choice 1 or 2?

Read Celsius

Read Fahrenheit

Multiply by 9/5, add 32

Subtract 32, multiply by 5/9

Show Fahrenheit

Show Celsius

3 The Build — explained part by part

Here is the complete converter. Read each part’s note below — you should understand the whole thing from the notes alone.

TypeScriptconverter.ts
import * as readline from "node:readline";
import { stdin, stdout } from "node:process";

// cToF converts Celsius to Fahrenheit.
export function cToF(celsius: number): number {
  return (celsius * 9) / 5 + 32;
}

// fToC converts Fahrenheit to Celsius.
export function fToC(fahrenheit: number): number {
  return ((fahrenheit - 32) * 5) / 9;
}

// ask prints a prompt and waits for one line of input. We read lines through
// the readline interface's own async iterator (rl's "for await" protocol)
// instead of its question()/promises API — pulled one at a time with
// it.next(), that iterator is the one part of readline that reliably keeps
// up when several prompts are answered in a row, typed by hand or piped in
// from a file.
function makeAsk(rl: readline.Interface) {
  const it = rl[Symbol.asyncIterator]();
  return async (prompt: string): Promise<string> => {
    stdout.write(prompt);
    const { value, done } = await it.next();
    return done ? "" : value;
  };
}

async function main(): Promise<void> {
  const rl = readline.createInterface({ input: stdin, terminal: false });
  const ask = makeAsk(rl);

  console.log("Temperature Converter");
  console.log("1. Celsius to Fahrenheit");
  console.log("2. Fahrenheit to Celsius");
  const choice = (await ask("Choose 1 or 2: ")).trim();

  if (choice === "1") {
    const celsius = Number((await ask("Enter temperature in Celsius: ")).trim());
    if (Number.isNaN(celsius)) {
      console.log("Please type a number.");
    } else {
      console.log(`${celsius}°C is ${cToF(celsius)}°F`);
    }
  } else if (choice === "2") {
    const fahrenheit = Number((await ask("Enter temperature in Fahrenheit: ")).trim());
    if (Number.isNaN(fahrenheit)) {
      console.log("Please type a number.");
    } else {
      console.log(`${fahrenheit}°F is ${fToC(fahrenheit)}°C`);
    }
  } else {
    console.log("Please choose 1 or 2.");
  }

  rl.close();
}

if (require.main === module) {
  main();
}
⚠ No in-browser playground here
Running real, type-checked TypeScript in the browser needs either a full copy of the compiler or a third-party CDN script — the same kind of external dependency this site avoids relying on for a core teaching example. Copy the code below and run it with Node on your own machine instead; the “Run It” section explains exactly how.
What each part does — in plain words
function cToF / function fToC — exactly like the Go version, each formula gets its own small, typed function. (celsius: number): number says “takes a number, returns a number” — TypeScript checks every call site against that promise.

readline.createInterface + an async iterator — Node has no built-in input(). We open a readline interface on stdin and pull one line at a time from its own async iterator (rl[Symbol.asyncIterator]()), awaiting it.next() each time we need an answer.

Number(answer.trim()) — converts the typed text to a number the same way Python’s float() does; unlike Python, a bad string does not throw — it silently becomes NaN, so we check with Number.isNaN(...) before using it.

celsius * 9 / 5 + 32 — identical maths to every other language here; JavaScript follows the same operator precedence.

if (require.main === module) — only runs main() when this file is executed directly, not when another file requires it (handy for the tests, which import cToF/fToC without starting the interactive program).
Common mistakes — and how to avoid them
✗ Calling rl.question() (the readline/promises API) twice in a row and expecting both typed answers to show up.
✓ When input arrives faster than you can register each listener — which piping a file into the program does — the promises-based question() can silently drop a line. Pulling lines from the interface’s own async iterator, one .next() per prompt, does not have this gap.
✗ Trusting Number("abc") to throw, the way Python’s float("abc") does.
✓ It returns NaN instead. Always check Number.isNaN(value) before using a number you parsed from text.
✗ Forgetting .trim() on a line read from stdin — a trailing \r on Windows-style input makes "1\r" !== "1".
✓ Trim every line before comparing it to an expected value.

4 Test & Prove Each Part

How do we know this works? We pull the real logic into small, plain functions and check each one against cases we already know the answer to.

Freezing point: 0°C correctly becomes 32°F
Boiling point: 100°C correctly becomes 212°F
The reverse works: 32°F correctly becomes 0°C
Round-trip: convert there and back, get the original number
TypeScriptconverter.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { cToF, fToC } from "./temperature-converter";

test("freezing point: 0C is 32F", () => {
  assert.equal(cToF(0), 32);
});

test("boiling point: 100C is 212F", () => {
  assert.equal(cToF(100), 212);
});

test("reverse freezing: 32F is 0C", () => {
  assert.equal(fToC(32), 0);
});

test("round trip returns the original number", () => {
  assert.ok(Math.abs(fToC(cToF(25)) - 25) < 0.00001);
});

Compile with npx tsc then run node --test converter.test.js (or skip the compile step while developing with npx tsx --test converter.test.ts). We pulled the formulas into small, independently-typed functions (cToF, fToC) so the tests can call them directly, without any menu or typed input in the way.

5 The Interface

The program’s interface — what it asks for and what it gives back — documented plainly.

INPUTchoice + temperature1 or 2, then a number
What it expects
Choose 1 or 2: 1
Enter temperature in Celsius: 100
OUTPUTconverted temperaturethe result with units
What it returns
100.0°C is 212.0°F

6 Run It & Automate It

Save the code as converter.ts. Compile it with npx tsc and run the result with node converter.js — or skip the separate compile step while experimenting with npx tsx converter.ts, which type-checks and runs in one command.

Run it locally
npx tsc converter.ts && node converter.js
Pick a direction, type a temperature, and see the conversion.

A CI tool like Jenkins runs the type-checker and tests automatically whenever the code changes — every line below has a plain explanation.

What you should see when it works
Terminala real run
Temperature Converter
1. Celsius to Fahrenheit
2. Fahrenheit to Celsius
Choose 1 or 2: 1
Enter temperature in Celsius: 100
100°C is 212°F
If it breaks — how to fix it
🚨 Please type a number. — even though I typed 100
Check for a trailing space or a stray character from copy-pasting; Number(" 100 ") is fine once trimmed, but Number("100cm") is NaN.
🚨 The second prompt never appears when I pipe input in with echo "1\n100" | node converter.js
This is the exact readline/promises trap described above. Make sure you are reading lines from the async iterator (as this program does), not calling rl.question() twice.
GroovyJenkinsfile
// Jenkinsfile &mdash; runs the type-checker and tests automatically every time the code changes.
pipeline {
    agent any                                  // run on any available machine

    stages {
        stage('Get the code') {
            steps { checkout scm }             // download the latest code
        }
        stage('Set up Node') {
            steps {
                sh 'node --version'                              // confirm Node is installed
                sh 'npm install -D typescript @types/node'       // zero runtime deps &mdash; just the compiler and its Node types
            }
        }
        stage('Type-check and test') {
            steps {
                sh 'npx tsc --noEmit'                 // catch type errors before anything runs
                sh 'npx tsc'                           // compile to plain JavaScript
                sh 'node --test converter.test.js'            // Node's built-in test runner, no extra install needed
            }
        }
    }

    post {
        success { echo 'All tests passed.' }
        failure { echo 'A test failed &mdash; look above.' }
    }
}
🎯 Try this next — make it yours

You have a working temperature converter. Extend it:

  1. Add Kelvin. Offer Celsius→Kelvin too (Kelvin = Celsius + 273.15). (Teaches: a third if branch.)
  2. Round differently. Use .toFixed(1) for a fixed one-decimal answer, or Math.round(...) for a whole number. (Teaches: Number formatting methods.)
  3. Keep converting. Wrap the whole thing in a loop so the user can do many conversions without restarting. (Teaches: an outer while loop around async code.)
  4. Guard bad input harder. Instead of giving up on a bad number, loop back and ask again. (Teaches: retry logic with while.)
What you learned
You learned the ask → calculate → show pattern behind every converter and calculator, plus typed functions with number parameters, NaN-checking, and testing maths against known facts with Node’s built-in test runner. Related reference: Basic Types, The TypeScript Compiler.