TypeScript BasicsBeginner7 min03 / 7

Typing Functions

Learn how to annotate function parameters and return types in TypeScript so your functions become self-documenting and error-proof by design.

Functions are the workhorses of any program — they take inputs, do something useful, and hand back a result. In plain JavaScript, a function will happily accept the wrong kind of input and silently do the wrong thing. TypeScript fixes that by letting you label every parameter and return value with a type. The moment you add those labels, TypeScript watches every call site and warns you before the code ever runs. That early warning system is one of the biggest reasons developers reach for TypeScript in the first place.

#Your First Typed Function

Adding types to a function is straightforward: write the type after the parameter name, separated by a colon, and write the return type after the closing parenthesis.

Parameter type comes after the colon next to the name; return type comes after the closing parenthesis.
function greet(name: string): string {
  return `Hello, ${name}!`;
}

console.log(greet("Ada"));
console.log(greet("World"));
Think of it like

A function signature is a contract

Think of a typed function like a vending machine with labelled slots. The slot says 'coins only' — you cannot shove in a credit card and expect a snack. The return slot is also labelled: it promises to give you a specific item, not a mystery box. TypeScript enforces both promises at compile time, before anyone uses the machine.

#Multiple Parameters and Numeric Types

Each parameter gets its own annotation. The commented line shows the kind of mistake TypeScript catches instantly.
function add(a: number, b: number): number {
  return a + b;
}

console.log(add(3, 7));
// add("3", 7); // TypeScript error: Argument of type 'string' is not assignable to parameter of type 'number'

#Optional Parameters with ?

Sometimes a parameter is genuinely optional — it has a sensible absence. Mark it with ? after the name. Inside the function, TypeScript will remind you that the value could be undefined, so you need to handle that case.

The ? makes title optional. Callers can omit it; the function handles both cases.
function greetUser(name: string, title?: string): string {
  if (title) {
    return `Hello, ${title} ${name}!`;
  }
  return `Hello, ${name}!`;
}

console.log(greetUser("Turing"));
console.log(greetUser("Turing", "Dr."));

#Default Parameters

A default parameter goes one step further: it provides a fallback value so you never have to guard against undefined inside the function. TypeScript infers the type from the default, so you usually do not need to write it explicitly.

When the caller omits prefix, TypeScript uses the default value. The return type is still string — no special handling needed.
function createMessage(text: string, prefix: string = "INFO"): string {
  return `[${prefix}] ${text}`;
}

console.log(createMessage("Server started"));
console.log(createMessage("Disk full", "WARN"));
Common mistake

Optional ? and default values are not interchangeable

A parameter with ? can be undefined inside the function body — you must check before using it. A parameter with a default value is never undefined inside the body, because TypeScript substitutes the default automatically. Reaching for a default is usually cleaner when you know what the fallback should be.

#void — Functions That Return Nothing

Not every function needs to return a value. A function that just prints to the console, saves data, or triggers an effect has a return type of void. Think of void as TypeScript's way of saying: "this function deliberately produces no result you should rely on."

void tells callers (and TypeScript) that there is no meaningful return value to capture.
function logScore(player: string, score: number): void {
  console.log(`${player} scored ${score} points.`);
  // no return statement needed
}

logScore("Alice", 4200);
logScore("Bob", 3100);

#Typed Arrow Functions

Arrow functions follow the exact same annotation rules. You can write the types inline, or let TypeScript infer the return type from the function body — both approaches are common in real codebases.

All three styles are valid. The third shows how to annotate the variable itself — useful when passing functions around.
// Explicit return type
const double = (n: number): number => n * 2;

// TypeScript infers the return type as number
const triple = (n: number) => n * 3;

// Arrow function stored in a typed variable
const greet: (name: string) => string = (name) => `Hi, ${name}!`;

console.log(double(5));
console.log(triple(5));
console.log(greet("Riya"));
Quick check

What does the ? symbol mean when placed after a function parameter name in TypeScript?

Tip

Let TypeScript infer return types when the body is simple

For short, obvious functions — a one-liner arrow, a simple transformation — you can omit the return type annotation and let TypeScript figure it out. Explicit return types shine on longer functions and public APIs, where the annotation serves as documentation and catches mistakes if the body ever changes.

Key takeaways

  • Annotate parameters as name: type and return values as function(): ReturnType to make functions self-documenting and type-safe.
  • Use ? for optional parameters (check for undefined inside the body) and = value for defaults (never undefined inside the body).
  • Use void as the return type for functions that perform side effects and return nothing meaningful.
  • Arrow functions follow the same annotation rules — types go on parameters, and the return type (if explicit) goes after the parameter list.
  • TypeScript checks every call site against the function signature, catching wrong argument types before the code runs.
Practice challenges
Test yourself · earn XP
0/4
Predict the output#1

A default parameter supplies a fallback when the caller omits it. What does this code print?

predict-output
function createMessage(text: string, prefix: string = "INFO"): string {
  return `[${prefix}] ${text}`;
}

console.log(createMessage("Server started"));
console.log(createMessage("Disk full", "WARN"));
Fix the bug#2

This code has a bug — what's wrong?

fix-bug
function greetUser(name: string, title?: string): string {
  return `Hello, ${title.toUpperCase()} ${name}!`;
}

greetUser("Turing");
Fill in the blank#3

This function only prints — it returns no meaningful value. Fill in the parameter type for score and the return type that tells callers there is nothing to capture.

function logScore(player: string, score: ):  {
  console.log(`${player} scored ${score} points.`);
}

logScore("Alice", 4200);
Reorder the lines#4

Arrange these lines to define a typed arrow function 'double' with an explicit number return type, then call it and log the result (10).

1
};
2
const double = (n: number): number => {
3
  return n * 2;
4
console.log(double(5));
Your turn
Practice exercise

Write a TypeScript function called formatPrice that takes a price (number) and an optional currency symbol (string). If no currency symbol is provided, use '$' as the default. The function should return a formatted string like '$9.99'. Then write a second arrow function called applyDiscount that takes a price and a discount percentage (both numbers) and returns the discounted price as a number. Finally, call both functions and log the results.

Try it live — edit the code and hit Run to see the output:

solution.ts · editable