Skip to content
Laviyx Learn

Try

TypeScript course menu

Typing Functions in TypeScript: Parameters, Return Types, Optional and Default Parameters

Learn how to type functions in TypeScript: parameter and return type annotations, optional parameters with ?, default values, rest parameters, arrow functions, void, typing callbacks, and destructured object parameters.

Beginner6 min readUpdated
On this page

Functions are where types help most: they define the contract between the code that calls a function and the code inside it.

Parameters and return types#

TypeScript
function applyDiscount(price: number, percent: number): number {
  return price - (price * percent) / 100;
}

const finalPrice = applyDiscount(1200, 15);
console.log(finalPrice);
Output
1020
  • Every parameter needs a type: price: number.
  • The return type goes after the parentheses: ): number. It is usually inferred, but writing it on important functions documents the intent and catches mistakes inside the function:
TypeScript
function isAdult(age: number): boolean {
  if (age >= 18) {
    return "yes";
  }
  return false;
}
Compiler error
error TS2322: Type 'string' is not assignable to type 'boolean'.

Callers must pass the right number and types of arguments, so applyDiscount(1200) and applyDiscount("1200", 15) are both errors.

Optional parameters#

A ? after a parameter name makes it optional. Inside the function, its type includes undefined:

TypeScript
function greet(name: string, title?: string): string {
  return title ? `Hello, ${title} ${name}` : `Hello, ${name}`;
}

console.log(greet("Rao"));
console.log(greet("Rao", "Dr."));
Output
Hello, Rao
Hello, Dr. Rao

Optional parameters must come after required ones.

Default values#

A default value makes the parameter optional, and its type is inferred from the default:

TypeScript
function formatPrice(amount: number, currency = "₹", decimals = 2): string {
  return `${currency}${amount.toFixed(decimals)}`;
}

console.log(formatPrice(499));
console.log(formatPrice(12.5, "$"));
console.log(formatPrice(499, "₹", 0));
Output
₹499.00
$12.50
₹499

Rest parameters#

A rest parameter collects extra arguments into an array, typed as an array:

TypeScript
function sum(...numbers: number[]): number {
  return numbers.reduce((total, n) => total + n, 0);
}

console.log(sum(), sum(5), sum(1, 2, 3, 4));
Output
0 5 10

Arrow functions#

Annotations work the same way on arrow functions:

TypeScript
const square = (n: number): number => n * n;
const isEven = (n: number) => n % 2 === 0;    // return type inferred as boolean

console.log(square(7), isEven(7));
Output
49 false

void#

void means a function does not return a useful value:

TypeScript
function log(message: string): void {
  console.log(`[log] ${message}`);
}
log("Server started");
Output
[log] Server started

Functions as parameters#

To accept a callback, describe its type with arrow syntax: (parameters) => returnType:

TypeScript
function repeat(times: number, action: (i: number) => void): void {
  for (let i = 1; i <= times; i++) action(i);
}

repeat(3, (i) => console.log(`Step ${i}`));
Output
Step 1
Step 2
Step 3

The callback's parameter i did not need an annotation: TypeScript knew it from action's type (contextual typing). Function types are covered further in Function Types and Overloads.

Object parameters#

For functions with several options, take one object and destructure it. The type goes after the whole pattern:

TypeScript
function createUser({ name, age, isAdmin = false }: { name: string; age: number; isAdmin?: boolean }) {
  return `${name} (${age})${isAdmin ? " [admin]" : ""}`;
}

console.log(createUser({ name: "Asha", age: 30 }));
console.log(createUser({ name: "Ravi", age: 25, isAdmin: true }));
Output
Asha (30)
Ravi (25) [admin]

That inline type is getting long; giving it a name with an interface or type alias is cleaner. A common mistake is writing { name: string } inside the destructuring pattern itself, which renames the property instead of typing it.

Practice#

Practice exercise

Write calculateEmi(principal: number, annualRatePercent: number, months = 12): number that returns the monthly loan instalment using P * r * (1 + r)^n / ((1 + r)^n - 1), where r is the monthly rate as a decimal. Print the EMI for ₹1,00,000 at 12% a year for 12 months and for 24 months, rounded to whole rupees.

Show solution
TypeScript
function calculateEmi(principal: number, annualRatePercent: number, months = 12): number {
  const r = annualRatePercent / 12 / 100;
  const factor = (1 + r) ** months;
  return (principal * r * factor) / (factor - 1);
}

console.log(Math.round(calculateEmi(100000, 12)));
console.log(Math.round(calculateEmi(100000, 12, 24)));
Output
8885
4707

Key takeaway#

Finished this lesson?