Typing Parameters & Returns

Beginner · 16 min read · ▶ live playground · ✦ checkpoint

Typing function parameters means writing type promises for the inputs; typing returns means deciding what callers can expect back. In TypeScript, annotate parameters almost every time, let local return types be inferred, and write return types when a function boundary needs a stable promise.

Parameters are the input promises

A parameter is a named input in a function's parentheses. A parameter annotation is the colon-and-type promise after that name.

type UserProfile = {
  name: string;
  plan: string;
  karma: number;
};
 
function awardKarma(user: UserProfile, amount: number): string {
  const nextKarma = user.karma + amount;
  return user.name + " now has " + nextKarma + " karma on " + user.plan + ".";
}
 
const mina: UserProfile = {
  name: "Mina",
  plan: "pro",
  karma: 42,
};
 
console.log(awardKarma(mina, 8));

→ Mina now has 50 karma on pro.

The annotations sit at the boundary. user must be a UserProfile; amount must be a number; the function returns a string. Inside the body, nextKarma needs no annotation because inference can read the expression.

Think of the function boundary like the label on a container from Section 2. The label says what values may enter and what value leaves. Everything inside the container can still be checked without writing labels on every temporary value.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

The break-it line passes text into a slot promised to accept numbers. TypeScript rejects it before JavaScript runs. That is why beginners should not skip annotations on normal named parameters: amount has no initializer or right-hand-side value, so the checker cannot read your intent from one. Default parameters do have initializer values; the next lesson teaches that pattern.

Return inference is usually enough

A return type is the type after the parameter list that promises what a function gives back. Return type inference means TypeScript reads your return statements and works out that type for you.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

invoiceLine has no written return type, but it is still typed. The checker sees the string you return, so assigning the result to line: string works and assigning it to total: number fails.

This is the same rule you learned with variables: inference is not a weaker mode. It is the checker doing the work from the code you already wrote.

Write return types to freeze promises

The local helper above is fine with inference. But a boundary is the edge where one piece of code hands a value to another piece, and a public API is a boundary callers depend on. At a boundary, a written return type is not about helping TypeScript guess. It is about freezing the promise.

Here is that promise as an object return:

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

The explicit PlanSummary return type says exactly which properties callers may use. If a future edit tries to return only a formatted price string, the function itself turns red. That is future-proofing: the promise fails where it was broken.

When the return promise is wrong, TypeScript says so directly:

function planLabel(plan: string): string {
  if (plan === "pro") {
    return "Paid plan";
  }
 
  return 0;
}

→ playground.ts:6:3 - error TS2322: Type 'number' is not assignable to type 'string'.

Without the written : string, TypeScript would infer an either-or result from the two branches. Lesson 4.1 gives that shape its full name. For now, the rule is practical: when callers depend on a stable result, write the return type you intend.

Arrow functions get context

An arrow function is the (...) => ... function syntax from JavaScript. A callback is a function you pass as a value to another function. Contextual typing means TypeScript uses the surrounding code to type an expression.

That is why callbacks often need no parameter annotations:

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

launchYears is a number[], so map calls your callback with a number. The checker gives year that context. You do not need to write (year: number) here.

Keep this light for now: annotate named function parameters; let obvious callback parameters get context from the surrounding expression. Lesson 3.3 goes deep on function types and callbacks.

The rule to keep

Annotate function inputs. Let inference handle local return values. Write explicit return types when the function is a boundary: shared helpers, callbacks promised to another system, or a function whose callers should not be surprised by a future edit.

Next, function inputs get more flexible. Optional, default, and rest parameters let a boundary accept missing values, fallback values, and lists of values without giving up the type promises you just learned.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion