Skip to content
The Switch · JavaScript to TypeScript · Lesson 4

Arrays, tuples and functions

Lockers in a row, seats with meaning, and a coin slot.

Contents
Part 1 of 7 · The hook

Lockers, seats and a slot

Three everyday things, three kinds of label.

A row of school lockers: every one is the same size, and each holds the same kind of thing. A new locker can be added at the end. That is an array. You can say what is in it, as in "a row of number lockers".

A train ticket reads "Coach B2, seat 14". That is two things, in a fixed order, and the position tells you which is which. Coach comes first, seat second. Nobody writes it as "14, B2". That is a tuple: a short list with a fixed length, where each position has its own kind of value.

And a vending machine. You put in coins, and a snack falls out. The coin slot only takes coins. The tray only gives snacks. That is a function: what it takes in (its parameters, the named inputs) and what it gives back (its return value).

All three get labels in TypeScript, and the labels work the way these pictures suggest.

Remember: an array is lockers of one kind. A tuple is a fixed list where position matters. A function is a slot in and a tray out.

Part 2 of 7 · In JavaScript

Lockers with anything

JavaScript lets any kind into any list.

A JavaScript array is happy to hold any mixture. That is useful, and it is also how a total goes wrong.

JavaScriptOpen in TypeScript Playground (opens in a new tab)
const scores = [90, 85, 72];scores.push("77");let total = 0;for (const score of scores) {  total += score;}console.log(total);
Output
24777

We pushed the text "77" in by mistake, where we meant the number 77. After that, the running total isn't a number any more. Adding text to a number joins them, so we ended up with 24777 instead of 324. No error anywhere.

Pairs are also loose. A ticket is just an array, and nothing says how long it should be.

JavaScriptOpen in TypeScript Playground (opens in a new tab)
const ticket = ["B2", 14];const [coach, seat] = ticket;console.log(coach + ", seat " + seat);console.log(ticket[2]);
Output
B2, seat 14
undefined

Asking for item number 2 of a two-item list gives undefined. JavaScript treats "no such seat" as a value, not a mistake.

And a function's inputs have no slot shapes at all.

JavaScriptOpen in TypeScript Playground (opens in a new tab)
function greet(name, greeting = "Hello") {  return greeting + ", " + name;}console.log(greet("Asha"));console.log(greet("Asha", "Namaste"));
Output
Hello, Asha
Namaste, Asha

This one works. But the function doesn't say what name or greeting should be.

Remember: JavaScript arrays hold anything, pairs have no length, and function slots have no shape.

Part 3 of 7 · In TypeScript

Give every slot a shape

number[], tuples, and labelled parameters.

An array type is the item type with square brackets after it: number[] is "a list of numbers". Here is the scores program again.

const scores = [90, 85, 72];scores.push("77");let total = 0;for (const score of scores) {  total += score;}console.log(total);

Tinted lines are the ones that differ

push("77") is refused, because the list only accepts numbers. The mistake is caught where you made it, not where the total went strange. You can also write Array<number>, which means the same thing. Most people prefer number[].

A tuple type lists the type of each position, in order, in square brackets. You can add names to the positions to make it readable.

const ticket = ["B2", 14];const [coach, seat] = ticket;console.log(coach + ", seat " + seat);console.log(ticket[2]);

Tinted lines are the ones that differ

The type [coach: string, seat: number] has exactly two positions. Reading position 2 is refused, because there is no third thing. Destructuring (pulling the parts out into names, as in const [coach, seat] = ticket) works and gives each name its own type.

For functions, label the parameters. You can leave off the return type if you like, because TypeScript works it out from the code. Here is a function with a default parameter, one that takes a value if the caller leaves it out.

function greet(name, greeting = "Hello") {  return greeting + ", " + name;}console.log(greet("Asha"));console.log(greet("Asha", "Namaste"));

Tinted lines are the ones that differ

greeting = "Hello" is a default, and TypeScript infers from it that greeting is a string. You didn't need to write greeting: string = "Hello". A caller may leave it out, or pass their own.

A call with too many arguments is refused too.

TypeScriptOpen in TypeScript Playground (opens in a new tab)
function greet(name: string, greeting = "Hello"): string {  return greeting + ", " + name;}console.log(greet("Asha", "Namaste", "!"));

TypeScript says

error TS2554: Expected 1-2 arguments, but got 3.

Expected 1-2 arguments is TypeScript counting: one required, one optional.

Remember: number[] is a list of numbers, [string, number] is exactly a string and a number, and a default value gives you a type for free.

Part 4 of 7 · How it works

More ways to shape a slot

Optional, rest, callbacks and function types.

Optional parameters. A question mark after the name means the caller may leave it out. Inside the function the value may be undefined, so you check it before use.

TypeScriptOpen in TypeScript Playground (opens in a new tab)
function formatName(first: string, last?: string): string {  return last === undefined ? first : first + " " + last;}console.log(formatName("Asha"));console.log(formatName("Asha", "Rao"));
Output
Asha
Asha Rao

Use ? when "not given" is meaningful. Use a default (= "Hello") when "not given" should simply mean a standard value. An optional parameter has to come after the required ones.

Rest parameters. Three dots gather any number of extra arguments into an array. The label is an array type.

TypeScriptOpen in TypeScript Playground (opens in a new tab)
function sumAll(...numbers: number[]): number {  let sum = 0;  for (const n of numbers) {    sum += n;  }  return sum;}console.log(sumAll(1, 2, 3, 4));
Output
10

Callbacks. A callback is a function you hand to another function to call later. Methods like map take one. You don't need to label the callback's parameters when TypeScript can see what will be passed in.

TypeScriptOpen in TypeScript Playground (opens in a new tab)
const prices = [10, 20, 30];// The callback has no labels. TypeScript knows price is a number, because// prices is a number[], and map hands each item to the callback.const doubled = prices.map((price) => price * 2);const labels = prices.map((price) => "Rs " + price);

prices is a number[], so TypeScript knows each price that map hands over is a number. And since price * 2 is a number, doubled is a number[]. The second call turns each into text, so labels is a string[]. This is inference reading the place a function is used, which lesson 2 promised.

Function types. When a parameter is itself a function, you describe its slot and tray with an arrow.

TypeScriptOpen in TypeScript Playground (opens in a new tab)
type Discount = (price: number) => number;const tenOff: Discount = (price) => price * 0.9;function applyDiscount(price: number, discount: Discount): number {  return Math.round(discount(price));}console.log(applyDiscount(200, tenOff));
Output
180

(price: number) => number reads as "takes a number, gives back a number". Naming it with type Discount = ... lets you reuse it.

Remember: ? or = makes a parameter optional, ...name gathers the rest into an array, and (a: number) => number describes a function.

Part 5 of 7 · Watch out

Where lists and slots slip

Four small traps with arrays and functions.

readonly arrays. If a function should only read a list and never change it, give it a readonly list. Then push is refused.

TypeScriptOpen in TypeScript Playground (opens in a new tab)
const days: readonly string[] = ["Mon", "Tue"];days.push("Wed");

TypeScript says

error TS2339: Property 'push' does not exist on type 'readonly string[]'.

It is a good habit for function parameters. Callers know their list is safe.

Empty arrays need a label. A bare [] has no items to learn from. Write const names: string[] = [], so TypeScript knows what belongs in it.

A list isn't checked at the edge. scores[10] on a list of three gives undefined, but its type still says number. TypeScript trusts you not to read past the end. A stricter setting, noUncheckedIndexedAccess, changes that. For now, check the length or use a loop.

A tuple isn't a general array. [string, number] is for fixed pairs, like coordinates or a name and a score. If you find yourself writing a six-item tuple, an object from lesson 3 is a better fit, because its fields have names.

Remember: readonly for lists you only read, labelled empty arrays, and objects instead of long tuples.

Part 6 of 7 · Practise

Three functions, typed

A list, a pair, and a default.

Each problem starts with a JavaScript function. Open it in the Playground and type it.

Problem 1

Total of a list

Add types to total. total([10, 20, 30]) is 60. The call with a text item should be refused. Remove it.

Start here

JavaScriptOpen in TypeScript Playground (opens in a new tab)
function total(prices) {  let sum = 0;  for (const price of prices) {    sum += price;  }  return sum;}console.log(total([10, 20, 30])); // 60console.log(total([10, 20, "5"])); // "305", because "5" turns the sum into text

Stuck?

Show a solution
TypeScriptOpen in TypeScript Playground (opens in a new tab)
function total(prices: number[]): number {  let sum = 0;  for (const price of prices) {    sum += price;  }  return sum;}total([10, 20, 30]); // 60

prices: number[] says every item is a number. That makes the call with the text '5' an error, where JavaScript quietly turned the sum into the text 305. The variables sum and price are inferred, so they need no labels.

Problem 2

Smallest and largest

Add types to minMax, so it takes a list of numbers and gives back a pair: the smallest, then the largest.

Start here

JavaScriptOpen in TypeScript Playground (opens in a new tab)
function minMax(numbers) {  return [Math.min(...numbers), Math.max(...numbers)];}const [low, high] = minMax([3, 1, 2]);console.log(low, high); // 1 3

Stuck?

Show a solution
TypeScriptOpen in TypeScript Playground (opens in a new tab)
function minMax(numbers: number[]): [number, number] {  return [Math.min(...numbers), Math.max(...numbers)];}const [low, high] = minMax([3, 1, 2]); // 1 and 3

The return type [number, number] says exactly two numbers come back, the smallest first. Without it, TypeScript would infer number[], a list of any length, so the checker could not catch someone reading a third item. The tuple makes the length part of the promise.

Problem 3

Pad on the left

Add types to padLeft, and use a default parameter for fill instead of the || trick. padLeft("7", 3, "0") is "007", and padLeft("7", 3) is " 7".

Start here

JavaScriptOpen in TypeScript Playground (opens in a new tab)
function padLeft(text, width, fill) {  const missing = Math.max(0, width - text.length);  return (fill || " ").repeat(missing) + text;}console.log(padLeft("7", 3, "0")); // "007"console.log(padLeft("7", 3)); // "  7"

Stuck?

Show a solution
TypeScriptOpen in TypeScript Playground (opens in a new tab)
function padLeft(text: string, width: number, fill = " "): string {  const missing = Math.max(0, width - text.length);  return fill.repeat(missing) + text;}padLeft("7", 3, "0"); // "007"

text: string and width: number are required. fill = ' ' is a default, so TypeScript infers it as a string and callers may leave it out. A default replaces only a missing argument, so passing an empty text as the fill is respected. The old fill || ' ' replaced an empty fill too, which was a quiet bug.

Part 7 of 7 · Interview notes

Arrays and functions in interview

Syntax, tuples, and the optional versus default question.

These are the questions that sound easy and have a small catch.

  • "How do you type an array?" number[], or Array<number>. They mean the same thing.
  • "What is a tuple?" An array with a fixed length and a known type at each position, such as [string, number]. Use it for short, ordered pairs.
  • "What is the difference between an optional and a default parameter?" An optional parameter (last?: string) may be undefined inside the function, so you check it. A default parameter (greeting = "Hello") is filled in with a value when it is missing, so it is never undefined inside.
  • "Do you always write the return type?" No. TypeScript infers it. Many teams write it for public functions as documentation, and it makes the checker verify the promise.
  • "What does void mean as a return type?" The function doesn't return anything useful. It is used for functions that only do something, such as printing.
  • "What is a rest parameter?" ...numbers: number[] gathers any number of extra arguments into an array.
  • "How does TypeScript know the type of a callback's parameter?" From where the callback is used. In prices.map((price) => ...), prices is a number[], so price is a number.
  • "What does readonly string[] do?" It stops you from changing the array through that name: no push, no assigning to an index.
  • Say it simply. "T[] for lists, [A, B] for fixed pairs, label the inputs, and let the return type be inferred" covers it.

Updated 2026-10-17

Made by VeraStack Labs

Part 1

The Switch

JavaScript to TypeScript