Skip to Content
Course content

15: Typing Function Parameters and Returns

Click on the "Edit" button in the top corner of the screen to edit your slide content.

How do I actually tell TypeScript what types my function arguments should be?

It's pretty straightforward: you add the type annotation right after the parameter name in the parentheses. If you don't do this, TypeScript often defaults to any, which basically turns off the type-checker for that variable—and that's exactly what we're trying to avoid.

Let's say we're building a small system to calculate the total price of an item including tax. You want to make sure nobody accidentally passes a string into your math logic.

function calculateItemTotal(price: number, taxRate: number): number {
  return price + (price * taxRate);
}

// This works great
calculateItemTotal(100, 0.07); 

// TypeScript will yell at you here before you even run the code
calculateItemTotal("100", 0.07); 

By being explicit here, you're creating a contract. Anyone else on your team (including "future you" in three months) knows exactly what this function expects without having to dig through the implementation details.

Do I really need to type the return value, or does TypeScript just figure it out?

Technically, TypeScript is pretty smart. It uses something called "inference" to guess the return type based on the return statement. For a simple one-liner, it's usually spot on. But I strongly recommend typing your returns anyway, especially in professional projects.

Why? Because it prevents "accidental" returns. If you tell TypeScript the function must return a number, but you accidentally return a string or a boolean due to a logic error, TypeScript will catch it immediately. If you rely on inference, TypeScript might just change the inferred type of the function, and the bug will travel further down your call stack before it finally crashes.

// Explicitly stating this returns a string
function formatCurrency(amount: number): string {
  // If I accidentally returned 'amount' here without formatting, 
  // TS would throw an error because it's a number, not a string.
  return `$${amount.toFixed(2)}`;
}

What if my function could return a few different types of things?

This happens all the time. The most common scenario I run into is a "find" function: it either returns the object you're looking for, or it returns null because the object doesn't exist. You handle this using a Union Type (the | pipe symbol).

Here is a real-world example of searching a product list:

interface Product {
  id: string;
  name: string;
}

const inventory: Product[] = [
  { id: 'a1', name: 'Mechanical Keyboard' },
  { id: 'b2', name: 'Gaming Mouse' }
];

function findProductById(id: string): Product | null {
  const product = inventory.find(p => p.id === id);
  return product ? product : null;
}

const item = findProductById('z9');
// Now TypeScript forces you to check if 'item' is null 
// before you try to access item.name. This saves you from the dreaded "cannot read property of null" error.

How do I handle parameters that aren't always required?

You don't want to force the user to pass undefined or null just to satisfy the compiler. Instead, you use the optional parameter syntax by adding a question mark ? after the parameter name. Just remember: optional parameters must come after required ones.

Let's add a discount feature to our total calculator:

function calculateFinalPrice(price: number, discount: number = 0): number {
  return price - discount;
}

// Wait, I actually prefer optional parameters over defaults 
// when the logic changes based on presence:

function applyCoupon(price: number, couponCode?: string): number {
  if (couponCode === 'SAVE10') {
    return price * 0.9;
  }
  return price;
}

applyCoupon(100); // Valid
applyCoupon(100, 'SAVE10'); // Also valid



📋 Practical Task

Exercise: Refactoring the Order Summary Generator

You've been handed a piece of legacy JavaScript that has been converted to TypeScript, but it's currently using any everywhere. Your goal is to make it type-safe.

The Requirements:

  • Create an interface called Order with id (string), amount (number), and customerName (string).
  • Refactor the getOrderStatus function:
    • It should take an id (string) as a parameter.
    • It should return either an Order object or null if not found.
  • Refactor the formatOrderSummary function:
    • It should take an Order object as the first parameter.
    • It should take an optional currencySymbol (string) as the second parameter (defaulting to '$' if not provided).
    • It must return a string.
// Start with this messy code and fix the types:
function getOrderStatus(id) {
  const orders = [{ id: '101', amount: 50, customerName: 'Alice' }];
  return orders.find(o => o.id === id);
}

function formatOrderSummary(order, currencySymbol) {
  const symbol = currencySymbol || '$';
  return `${order.customerName} spent ${symbol}${order.amount}`;
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.