📖 The Story: The Silent Mutator

A team was building a data visualization dashboard. They had an array of numbers representing daily sales. A junior developer wrote a function to calculate the total, but they accidentally mutated the array inside the function instead of just reading it.

In JavaScript, the function ran silently. It returned the correct total, but it emptied the original array in the process. The next function that tried to chart the "daily sales" array found it empty, and the chart rendered blank. It took days to trace the bug back to the function that destroyed the data.

If they had used TypeScript, they could have typed the parameter as readonly number[]. The compiler would have immediately flagged the mutation: Property 'push' does not exist on type 'readonly number[]'. The bug would have been impossible to write.

JavaScript lets functions silently destroy data. TypeScript forces them to declare their intent.

🎯 Why Type Functions?

🛡️

Input Safety

Guarantee a function receives exactly the types it expects, no more, no less.

📦

Output Guarantees

Know exactly what a function returns, preventing undefined errors downstream.

📖

Self-Documenting

The function signature becomes a clear manual for how to use it.

🧒 Explain Like I'm 10

Imagine a vending machine. The slot only accepts quarters (25 cents). If you try to put in a dime (10 cents), it rejects it.

A function is the vending machine. The parameters are the coin slot. The return type is the snack that drops out. TypeScript is the bouncer standing next to the machine, making sure you only put in quarters and ensuring the machine actually drops a snack.

🎓 Professional Explanation

A function type consists of two parts: the parameter types and the return type. When you annotate a function, TypeScript checks that the arguments passed match the parameter types, and it ensures that every code path inside the function returns the correct type.

⚙️ Basic Syntax

typescript
// function name(parameter: type): returnType
function add(a: number, b: number): number {
  return a + b;
}

// Arrow function syntax
const multiply = (a: number, b: number): number => a * b;

Return Type Inference: TypeScript can infer the return type automatically. However, explicitly writing it is a best practice for public functions to ensure you don't accidentally change the return type in the future.

🔒 Optional & Default Parameters

Like interfaces, you can make parameters optional with ?. If you provide a default value, TypeScript infers the type and makes it optional automatically.

typescript
// Optional parameter (must come last)
function greet(name: string, greeting?: string): string {
  return `${greeting || "Hello"}, ${name}!`;
}

// Default parameter (type inferred as string)
function greetDefault(name: string, greeting = "Hello"): string {
  return `${greeting}, ${name}!`;
}

📦 Rest Parameters

Rest parameters allow you to pass an indefinite number of arguments. They are typed as an array.

typescript
function sumAll(...numbers: number[]): number {
  return numbers.reduce((total, num) => total + num, 0);
}

sumAll(1, 2, 3); // 6
sumAll(1, "2"); // Error: 'string' is not assignable to 'number'

📞 Typing Callbacks

Callbacks are functions passed as arguments. Typing them is critical for asynchronous code and event handlers.

typescript
function fetchData(url: string, callback: (data: string) => void) {
  // Simulate fetch
  setTimeout(() => {
    callback("User Data");
  }, 1000);
}

// Valid
fetchData("/api", (data) => console.log(data.length));

// Error: Expected 1 arguments, but got 0
fetchData("/api", () => console.log("Done")); 

🚫 The void Type

void is the return type for functions that do not return a value. It tells TypeScript, "I expect this function to be used for its side effects (like logging or mutating state), not for a return value."

💡 void vs undefined

If a function is typed to return undefined, it must explicitly return undefined. If it is typed to return void, it can return nothing, or return any value (which will be ignored). This is why void is preferred for callbacks.

⚠️ Common Mistakes

Mistake 1: Optional Parameters Before Required

typescript · Bad
// Error: A required parameter cannot follow an optional parameter.
function build(type?: string, count: number) { ... }

✅ Best Practices

  1. Explicit Return Types: Always write the return type for exported/public functions.
  2. Use void for Callbacks: It prevents you from accidentally using the return value of a callback.
  3. Readonly Arrays: If a function shouldn't mutate an array parameter, type it as readonly number[].

🏗️ Senior Deep Dive

Function Type Aliases

In complex codebases, writing inline callback types gets messy. You can extract them into a Type Alias.

typescript
type MapCallback<T, U> = (item: T, index: number) => U;

function customMap<T, U>(arr: T[], cb: MapCallback<T, U>): U[] {
  return arr.map(cb);
}

💼 Interview Questions

Beginner
What is the difference between void and undefined in a return type?
Show Answer
undefined requires the function to explicitly return undefined. void means the function returns nothing, and any return value it might have will be ignored by the caller.
Intermediate
How do you type a function that can accept any number of arguments of the same type?
Show Answer
Using rest parameters, e.g., function sum(...numbers: number[]): number.

🏋️ Practical Exercise

🎯 Hands-On Practice Easy

Type the Function

Add type annotations to this function. It should take a string and a number, and return a string.

javascript">
function repeatText(text, times) {
  return text.repeat(times);
}

Task: Add types to text, times, and the return type.

📝 Quiz

Test your understanding. Click an option to check your answer.

1. Which return type should you use for a function that performs an action but doesn't return a value?
A) any
B) null
C) void
Answer: C — void indicates the function is meant for side effects, not a return value.

📦 Summary

🎯 Key Takeaways

  • Type parameters to ensure functions receive the correct input.
  • Explicitly type return values for public functions.
  • Use ? for optional parameters and = for default parameters.
  • Type callbacks using inline signatures or type aliases.
  • Use void for functions that don't return a value.