Skip to content
aviral gupta

// B2.5 · ~37 min · Beginner

Build: a unit converter

After this build you can turn text inputs into numbers, check them, convert them with named constants and print tidy results such as 42.195 km = 26.22 mi.

Lesson 5 of 5 in B2 Grammar, types and operators

End of the module

You will be able to

  • Keep conversion factors in named constants and convert with the right formula
  • Format results with toFixed and template literals
  • Check input values with typeof, Number() and Number.isNaN before converting them
  1. Warm-up · Activity 1 of 7

    Warm-up: which of these values belongs in a const with a name in capitals, such as KM_PER_MILE?

  2. Predict · Activity 2 of 7

    Predict before you read on: the input arrives as text. What does this print?

    const km = "10";
    console.log(km + 5, Number(km) + 5);
  3. Practice · Activity 3 of 7

    Fill in the method that rounds the miles to two decimals, so that the program prints 6.21.

    const KM_PER_MILE = 1.609344;
    const km = 10;
    const miles = km / KM_PER_MILE;
    console.log(miles.____(2));
    console.log(miles.(2));
  4. Practice · Activity 4 of 7

    What does this print?

    console.log(typeof "12", typeof Number("12"), typeof Number("12 km"));
  5. Practice · Activity 5 of 7

    Match each expression to what console.log prints for it.

  6. Brain teaser · Activity 6 of 7

    Brain teaser. What does this print?

    const total = 0.1 + 0.2;
    console.log(total.toFixed(2), total.toFixed(2) === 0.3);
  7. Apply · Activity 7 of 7

    Mini-task. Write the whole converter from scratch: the two factors as constants, three inputs as text (a distance, a mass and a temperature), their types before and after Number(), one line per conversion formatted with toFixed and a template literal, and a last line that checks a bad input such as "12 km" with Number.isNaN. Then change an input and predict the output.

    Check your work against this list

Build it yourself

Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.

Worked example

The unit converter

The three inputs are written into the program as text, because the editor has no keyboard input; a form on a page would give text too. The program shows the type before and after Number(), converts each value with a named factor or formula, and prints tidy results. The last line shows what a bad input turns into. Change an input, or the number of decimals, and predict the output before you run it again.

main.js

// A unit converter. The inputs are text, as a form field would give them.
const KM_PER_MILE = 1.609344;
const KG_PER_POUND = 0.45359237;

const distanceInput = "42.195";
const massInput = "70";
const celsiusInput = "21.5";

console.log(`Before: ${typeof distanceInput}`);
const km = Number(distanceInput);
const kg = Number(massInput);
const celsius = Number(celsiusInput);
console.log(`After: ${typeof km}`);

console.log(`${km} km = ${(km / KM_PER_MILE).toFixed(2)} mi`);
console.log(`${kg} kg = ${(kg / KG_PER_POUND).toFixed(1)} lb`);
console.log(`${celsius} °C = ${(celsius * 9 / 5 + 32).toFixed(1)} °F`);

const badInput = "12 km";
const bad = Number(badInput);
console.log(`"${badInput}" is NaN: ${Number.isNaN(bad)}`);

Run it with

node main.js

Output

Before: string
After: number
42.195 km = 26.22 mi
70 kg = 154.3 lb
21.5 °C = 70.7 °F
"12 km" is NaN: true
  • typeof shows the change: the input is a string, the converted value a number.
  • Each factor is written once, in a const, and used by name.
  • toFixed(2) and toFixed(1) round only what is printed; km and kg keep their full values.
  • "12 km" is not a number as a whole, so Number gives NaN, and Number.isNaN reports it.
Change it and run it

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Exercises

Exercise 1 of 3

Step 1: kilometres to miles

Convert two distances, given as text, into miles: "10" and "21.0975". Convert each input with Number(), divide by KM_PER_MILE, and print one line each, with the miles rounded to two decimals: 10 km = 6.21 mi and 21.0975 km = 13.11 mi. Use a template literal for each line.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    Run the starter: the miles come out as 6.2137119223733395. toFixed(2) rounds that to 6.21.

  2. Hint 2

    Put brackets around the division before .toFixed: (km / KM_PER_MILE).toFixed(2).

  3. Hint 3

    A template literal builds the line: `${km} km = ${(km / KM_PER_MILE).toFixed(2)} mi`. Do the same for the second input.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

const KM_PER_MILE = 1.609344;
const distanceInput = "10";
const halfMarathonInput = "21.0975";

const km = Number(distanceInput);
console.log(`${km} km = ${(km / KM_PER_MILE).toFixed(2)} mi`);
const halfKm = Number(halfMarathonInput);
console.log(`${halfKm} km = ${(halfKm / KM_PER_MILE).toFixed(2)} mi`);
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

const KM_PER_MILE = 1.609344;
const distanceInput = "10";
const halfMarathonInput = "21.0975";

// Print: 10 km = 6.21 mi
console.log(distanceInput, "km =", distanceInput / KM_PER_MILE, "mi");

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runMain} from './learnrun.js';

const lines = async () => (await runMain()).trimEnd().split('\n');

test('converts 10 km', async () => {
  const got = await lines();
  assert.equal(got[0], '10 km = 6.21 mi', `the first line is ${JSON.stringify(got[0])}, expected "10 km = 6.21 mi"`);
});

test('converts the half marathon', async () => {
  const got = await lines();
  assert.equal(got[1], '21.0975 km = 13.11 mi', `the second line is ${JSON.stringify(got[1])}, expected "21.0975 km = 13.11 mi"`);
});

test('prints exactly two lines', async () => {
  const got = await lines();
  assert.equal(got.length, 2, `the program printed ${got.length} line(s), expected 2`);
});

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Exercise 2 of 3

Step 2: check the inputs

Before converting, show what the inputs are. Print massInput: string (the type of the input), convert it with Number() and print kg: number, then print the mass in pounds with one decimal: 70 kg = 154.3 lb. Last, convert heightInput and print "1,80" is NaN: true, with the input in double quotes and the check done by Number.isNaN.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    typeof massInput gives "string"; in a template literal: `massInput: ${typeof massInput}`.

  2. Hint 2

    A pound is KG_PER_POUND kilograms, so pounds are kg / KG_PER_POUND. Round with .toFixed(1).

  3. Hint 3

    Double quotes can stand inside backticks as they are: `"${heightInput}" is NaN: ${Number.isNaN(height)}`.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

const KG_PER_POUND = 0.45359237;
const massInput = "70";
const heightInput = "1,80";

console.log(`massInput: ${typeof massInput}`);
const kg = Number(massInput);
console.log(`kg: ${typeof kg}`);
console.log(`${kg} kg = ${(kg / KG_PER_POUND).toFixed(1)} lb`);

const height = Number(heightInput);
console.log(`"${heightInput}" is NaN: ${Number.isNaN(height)}`);
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

const KG_PER_POUND = 0.45359237;
const massInput = "70";
const heightInput = "1,80";

// 1. Print the type of massInput, convert it, print the type of kg.
// 2. Print the mass in pounds, with one decimal.
// 3. Convert heightInput and print whether the result is NaN.

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runMain} from './learnrun.js';

const lines = async () => (await runMain()).trimEnd().split('\n');

test('shows the types before and after Number()', async () => {
  const got = await lines();
  assert.deepEqual(got.slice(0, 2), ['massInput: string', 'kg: number'], `the first two lines are ${JSON.stringify(got.slice(0, 2))}`);
});

test('converts 70 kg to pounds', async () => {
  const got = await lines();
  assert.equal(got[2], '70 kg = 154.3 lb', `the third line is ${JSON.stringify(got[2])}, expected "70 kg = 154.3 lb"`);
});

test('finds that "1,80" is not a number', async () => {
  const got = await lines();
  assert.equal(got[3], '"1,80" is NaN: true', `the fourth line is ${JSON.stringify(got[3])}, expected ${JSON.stringify('"1,80" is NaN: true')}`);
});

test('prints exactly four lines', async () => {
  const got = await lines();
  assert.equal(got.length, 4, `the program printed ${got.length} line(s), expected 4`);
});

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Exercise 3 of 3

Step 3: temperatures both ways

This part converts 21.5 °C to Fahrenheit and 98.6 °F to Celsius, but the second line is wrong and neither is tidy. It should print 21.5 °C = 70.7 °F and 98.6 °F = 37.0 °C. Fix the order of the calculation in the second formula and round both results to one decimal. There is no error message: compare the output with what it should be.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    The starter prints 80.82222222222222 for 98.6 °F: 32 * 5 / 9 is worked out first, and then subtracted.

  2. Hint 2

    Brackets decide the order: (fahrenheit - 32) * 5 / 9.

  3. Hint 3

    Wrap each whole calculation in brackets and add .toFixed(1): 37 becomes "37.0".

Show a solution

One way to solve it. Yours can look different and still pass the checks.

const celsiusInput = "21.5";
const fahrenheitInput = "98.6";
const celsius = Number(celsiusInput);
const fahrenheit = Number(fahrenheitInput);

console.log(`${celsius} °C = ${(celsius * 9 / 5 + 32).toFixed(1)} °F`);
console.log(`${fahrenheit} °F = ${((fahrenheit - 32) * 5 / 9).toFixed(1)} °C`);
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

const celsiusInput = "21.5";
const fahrenheitInput = "98.6";
const celsius = Number(celsiusInput);
const fahrenheit = Number(fahrenheitInput);

console.log(`${celsius} °C = ${celsius * 9 / 5 + 32} °F`);
console.log(`${fahrenheit} °F = ${fahrenheit - 32 * 5 / 9} °C`);

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runMain} from './learnrun.js';

const lines = async () => (await runMain()).trimEnd().split('\n');

test('converts 21.5 °C to Fahrenheit', async () => {
  const got = await lines();
  assert.equal(got[0], '21.5 °C = 70.7 °F', `the first line is ${JSON.stringify(got[0])}, expected "21.5 °C = 70.7 °F"`);
});

test('converts 98.6 °F to Celsius, with one decimal', async () => {
  const got = await lines();
  assert.equal(got[1], '98.6 °F = 37.0 °C', `the second line is ${JSON.stringify(got[1])}, expected "98.6 °F = 37.0 °C"`);
});

test('prints exactly two lines', async () => {
  const got = await lines();
  assert.equal(got.length, 2, `the program printed ${got.length} line(s), expected 2`);
});

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Common mistakes

toFixed on the text input

const distanceInput = "42.195";
console.log(distanceInput.toFixed(2));

What Node.js prints

TypeError: distanceInput.toFixed is not a function

Why, and the fix

toFixed is a method of numbers, and distanceInput is still a string, which has no toFixed. Convert first, then format: Number(distanceInput).toFixed(2), or better, keep the number in its own const km = Number(distanceInput); and call km.toFixed(2) when you print.

Changing a conversion factor

const KM_PER_MILE = 1.609344;
KM_PER_MILE = 1.6;

What Node.js prints

TypeError: Assignment to constant variable.

Why, and the fix

A const cannot be given a new value, and that is the point of a factor in a const: no line can round or change it by accident. If you want a shorter number in the output, round the result with toFixed when you print it, and leave the factor as it is.

A template literal without its closing backtick

const km = 5;
console.log(`${km} km);

What Node.js prints

SyntaxError: Unexpected end of input

Why, and the fix

A template literal starts and ends with a backtick. Without the closing one, JavaScript reads the rest of the file as part of the text and reaches the end still inside it. As with every SyntaxError, no line runs. Close it: console.log(`${km} km`);.

JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source

Exit ticket

5 questions, no hints. Score 80% or more to complete the lesson.

Finish every activity above to unlock the exit ticket.

Report a problem

Spotted something wrong or unclear? Say what, and it will be checked and fixed.

#

At least 20 characters.

Only if you want a reply.

Key ideas

Constants for the factors, one formula per conversion

A mile is 1.609344 km and a pound 0.45359237 kg. Put such factors in a const with a name in capitals, such as KM_PER_MILE: the name says what the number means, the value sits in one place, and a line that tries to change it stops the program with a TypeError. The name also tells you the direction: km / KM_PER_MILE gives miles, and miles * KM_PER_MILE gives kilometres. Temperatures need a formula: celsius * 9 / 5 + 32 gives Fahrenheit, and (fahrenheit - 32) * 5 / 9 gives Celsius. Without the brackets, * and / go first, and the result is wrong.

Text in, number out: typeof, Number() and NaN

Values from a form field arrive as text: typeof "42.195" is "string". Number("42.195") turns it into the number 42.195, and typeof then gives "number". Without Number(), + joins text: "10" + 5 is "105". The whole text must be a number, with a point for decimals, so Number("12 km") and Number("1,80") both give NaN. Check a result with Number.isNaN before you trust it. One trap: Number("") gives 0, not NaN, so an empty field looks like the number zero.

Tidy output: toFixed and template literals

Division rarely gives a round number: 10 / KM_PER_MILE is 6.2137119223733395. (6.2137119223733395).toFixed(2) rounds it to two decimals and pads with zeros where needed, so (37).toFixed(1) is "37.0". toFixed returns a string, so do all the maths first and call it last, when you print. Put brackets around the calculation: (km / KM_PER_MILE).toFixed(2). A template literal, in backticks, puts the values into the sentence: `${km} km = ${miles} mi`. Each ${…} can hold any expression.

Sources

Last reviewed September 30, 2026