Parameters and Return Values

Think of a subroutine as a little machine. A vending machine is useless if it can only ever hand you one fixed can of drink — you want to tell it which button you pressed, and get back whatever you chose. A subroutine works the same way: you feed data in, it does its job, and it passes a result back out.

Those two channels have names, and they are the whole of this page:

Together they turn a subroutine from a one-trick gadget into a reusable tool: one subroutine that works for many different inputs. Write it once, call it a thousand times with a thousand different values.

Without parameters, a subroutine is stuck

Here is a subroutine that finds the area of a rectangle — but only one rectangle, because the numbers 4 and 3 are baked in. To find a different area you'd have to write a whole new subroutine. That is exactly the copy-paste trap we want to escape:

function areaOfFixedRectangle(): number { return 4 * 3; // always the same rectangle — not much use! }

Now give it two parameters, w and h. The values are no longer fixed — they arrive when the subroutine is called. Press Run and watch one subroutine handle three completely different rectangles:

function areaOfRectangle(w: number, h: number): number { return w * h; } console.log(areaOfRectangle(4, 3)); // a 4 by 3 rectangle console.log(areaOfRectangle(10, 2)); // a long thin one console.log(areaOfRectangle(7, 7)); // a square

The bit in the definition, (w: number, h: number), is the parameter list: two named boxes that will hold whatever numbers get passed in. The : number after each name is its type — it promises this box will hold a number — and the : number after the brackets promises the subroutine returns a number too.

Parameter or argument? They are not the same word

People use these two words loosely, but in an exam they mean precise, different things — and it's easy marks to get them the right way round:

A neat way to remember it: the parameter is part of the plan (the definition), and the argument is the actual value you argue for at call time. When the call runs, each argument is copied into its matching parameter, left to right.

In the definition function celsiusToFahrenheit(c: number), the name c is the parameter. When you write the call celsiusToFahrenheit(20), the value 20 is the argument. As the subroutine runs, c is 20 — the argument has been copied into the parameter. Call it again with celsiusToFahrenheit(100) and the very same parameter c now holds 100. One slot, refilled on every call.

Returning a value: sending an answer back out

A parameter carries data in; return carries a value out. The moment a return runs, the subroutine stops and hands its value back to the line that called it — where it can be printed, stored in a variable, or used in a bigger sum.

This celsiusToFahrenheit subroutine takes a temperature in Celsius and returns it in Fahrenheit, using F = \tfrac{9}{5}C + 32. Notice the returned value being captured into a variable, and also used directly inside a console.log:

function celsiusToFahrenheit(c: number): number { return (c * 9) / 5 + 32; } const bodyTemp = celsiusToFahrenheit(37); // capture the returned value console.log("Body temperature is " + bodyTemp + "°F"); console.log("Water boils at " + celsiusToFahrenheit(100) + "°F"); // use it directly console.log("Water freezes at " + celsiusToFahrenheit(0) + "°F");

A returned value doesn't have to be a number. Here isPositive takes a number and returns a booleantrue or false — which is perfect for asking a yes/no question:

function isPositive(n: number): boolean { return n > 0; } console.log(isPositive(5)); // true console.log(isPositive(-3)); // false console.log(isPositive(0)); // false — zero is not positive

Putting them together: input in, answer out

The real power shows when the returned value of one call feeds straight into another piece of logic. Below, isPositive is called inside a loop — the same tiny subroutine reused on a whole list of numbers, each time with a different argument:

function isPositive(n: number): boolean { return n > 0; } const readings: number[] = [12, -4, 0, 8, -1]; for (const r of readings) { if (isPositive(r)) { console.log(r + " is positive"); } else { console.log(r + " is not positive"); } }

Five different arguments, one subroutine. That is the whole point of parameters and return values: you describe how to solve the problem once, then let the data vary.

1. Forgetting to return. A subroutine that never runs a return statement doesn't hand back nothing-in-particular — it hands back the special value undefined. This trips up beginners constantly, because the code looks like it works: it does the calculation, it just never gives the answer back.

function areaOfRectangle(w: number, h: number): number { const area = w * h; // BUG: the answer is worked out... // ...but never returned! This function gives back `undefined`. }

The fix is one line — return area; — and TypeScript will actually warn you here, because you promised to return a number but never did. If a call seems to give back nothing useful, check that every path through the subroutine ends in a return.

2. Argument order matters. Arguments fill the parameters left to right, so swapping two arguments swaps their meaning. For a subtraction or a division, that changes the answer completely:

function divide(a: number, b: number): number { return a / b; } console.log(divide(10, 2)); // 5 — ten divided by two console.log(divide(2, 10)); // 0.2 — two divided by ten. Not the same!

divide(10, 2) is not the same as divide(2, 10). The computer will never guess what you meant — it just pours the first argument into the first parameter. Get the order right, every time.