Free Handbook · Runs in your browser

Functions

Parameters, optional and default and rest arguments, overloads, typing this, void versus never, and functions as typed values.

0 / 105 lessons🔥 0 day streak
ShareXLinkedIn

Module 03 · what you'll be able to do

  • Annotate function parameters and choose between optional, default, and rest parameters
  • Write function overloads and explain how TypeScript picks a matching signature
  • Type the this value inside a function so a detached call is caught at compile time
  • Tell void and never apart and know which one describes a function that always throws
  • Store and pass functions as typed values with a named function type
01

Parameter types

Every parameter in a TypeScript function should carry an explicit type — the compiler will not infer one for you from thin air, so an unannotated parameter is flagged as an implicit any under standard settings. The same rule applies to arrow functions assigned to a const, whether they are declared with a block body or as a single expression.

typescriptEdit it. ⌘/Ctrl + Enter runs.
You should see
20
5
Your turn
Add a third parameter c: number to multiply and use it in the return expression.
Error you will hit

TS2554: calling with the wrong number of arguments

typescript
function multiply(a: number, b: number): number {
  return a * b
}
multiply(4)
app.ts:4:1 - error TS2554: Expected 2 arguments, but got 1.

4 multiply(4)
  ~~~~~~~~~~~
Why the compiler said that

Both parameters are required — neither has a ? or a default value — so the function's type demands exactly two arguments at every call site.

The fix

Pass both arguments, or make the second one optional if calling with only one genuinely makes sense.

typescript
function multiply(a: number, b: number): number {
  return a * b
}
multiply(4, 5)
Return type is optional, parameters are not
You can drop : number after multiply(a: number, b: number) and TypeScript infers it from return a * b — but the parameter types themselves cannot be dropped the same way, because there is no call yet for the compiler to infer them from.
02

Optional (?), default, and rest parameters

A parameter marked with ? is optional — callers may omit it, and inside the function its type is unioned with undefined. A default parameter (greeting: string = "Hello") is also optional to callers, but instead of becoming undefined when omitted, it falls back to the given value. A rest parameter (...args: number[]) gathers any number of trailing arguments into a single typed array, and must be the last parameter in the list.

typescriptEdit it. ⌘/Ctrl + Enter runs.
You should see
Hello, Ada
Hi, Ada
9
1024
10
Your turn
Call sum() with no arguments at all — nums is just an empty array, and reduce with an initial value handles it fine.
Error you will hit

TS1016: a required parameter after an optional one

typescript
function greet(greeting?: string, name: string) {
  return `${greeting}, ${name}`
}
app.ts:1:33 - error TS1016: A required parameter cannot follow an optional parameter.

1 function greet(greeting?: string, name: string) {
                                    ~~~~
Why the compiler said that

If callers could omit an earlier parameter but not a later one, JavaScript would have no way to tell which argument they meant to skip — there is no named-argument syntax, only position. TypeScript refuses the ordering outright rather than allow a function no one could call unambiguously.

The fix

Put every required parameter before the optional ones.

typescript
function greet(name: string, greeting?: string) {
  return `${greeting}, ${name}`
}
KindSyntaxMissing at call siteOrder rule
Optionalname?: stringBecomes undefinedMust come after all required parameters
Defaultname: string = "x"Falls back to the given valueUsually placed after required parameters, though TypeScript allows earlier positions if callers pass undefined explicitly
Rest...names: string[]Becomes an empty array, never undefinedMust be the very last parameter
JuniorWhat is the practical difference between an optional parameter and a default parameter?

Both let a caller omit the argument. An optional parameter (name?: T) becomes undefined inside the function when omitted, and its type inside the body is T | undefined — you generally need to handle the missing case yourself. A default parameter (name: T = value) is also optional to call, but the function substitutes the given value when it is omitted, so inside the body its type is just T, with no undefined case to handle.

What they are really testing: Whether the candidate knows the type difference inside the function body, not just that both are 'optional-ish'.

03

Function overloads

An overload lets one function name accept genuinely different call shapes, each with its own precise parameter and return types, backed by a single implementation. You write one or more overload signatures — declarations with no body — followed by an implementation signature whose body actually runs. The implementation signature is usually looser (often using any) because it has to satisfy every overload above it; crucially, callers never see the implementation signature — only the overload list is used to check a call.

typescriptoverloads.ts
function combine(a: string, b: string): string
function combine(a: number, b: number): number
function combine(a: any, b: any): any {
  return a + b
}

const text = combine("Type", "Script")   // typed as string
const total = combine(2, 3)              // typed as number

The two lines with no body are overload signatures — pure type declarations, erased at compile time. Only the third, with a body, is the real implementation, and it is the only one that produces any JavaScript.

typescriptEdit it. ⌘/Ctrl + Enter runs.
You should see
TypeScript
5
Your turn
In the code block above (not this one), add a third overload for two booleans that returns a string like "true and false", then write the implementation logic for it.
VisualizeWhich overload signature a call resolves toStep 1 / 5
function combine(a: any, b: any): any {
return a + b
}
console.log(combine("Type", "Script"))
console.log(combine(2, 3))
Line 1

The implementation accepts anything (any, any) — but callers never check a call against this signature directly, only against the overload declarations listed above it.

Variables now

nothing yet

All 5 steps as a table
StepLineWhat happenedVariables now
11The implementation accepts anything (any, any) — but callers never check a call against this signature directly, only against the overload declarations listed above it.
24TypeScript checks each call against the overload list from top to bottom and picks the first one whose parameter types match. combine("Type", "Script") matches overload 1, (a: string, b: string) => string, so this call is typed as returning string.
34At runtime it is just the real implementation running: a + b on two strings is concatenation.
45combine(2, 3) does not match overload 1 (numbers are not strings), so TypeScript tries overload 2, (a: number, b: number) => number, and it matches — this call is typed as returning number.
55At runtime, a + b on two numbers is addition.
Error you will hit

TS2769: no overload matches this call

typescript
function combine(a: string, b: string): string
function combine(a: number, b: number): number
function combine(a: any, b: any): any {
  return a + b
}

combine("a", 2)
app.ts:6:1 - error TS2769: No overload matches this call.
  Overload 1 of 2, '(a: string, b: string): string', gave the following error.
    Argument of type 'number' is not assignable to parameter of type 'string'.
  Overload 2 of 2, '(a: number, b: number): number', gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'number'.

6 combine("a", 2)
  ~~~~~~~~~~~~~~~
Why the compiler said that

Neither overload accepts one string and one number — overload 1 wants two strings, overload 2 wants two numbers, and mixing them satisfies neither. The implementation signature never even gets consulted for this check; only the declared overloads decide what a caller may pass.

The fix

Pass arguments matching one declared overload, or add a third overload if mixed types genuinely need to be supported.

typescript
combine("a", "2")
Order matters
Because resolution stops at the first matching overload, a looser signature placed before a stricter one can shadow it — put more specific overloads first, and the broadest, most permissive one last.
04

Typing this in a function

JavaScript's this is famously dynamic — it depends on how a function is called, not where it is defined, which is a common source of runtime bugs when a method is passed around and loses its intended receiver. TypeScript lets you pin down what this should be with a special fake first parameter named this: function onClick(this: Button) { ... }. It is checked at compile time and, like every other type annotation, completely erased — it never shows up as a real parameter in the compiled JavaScript.

typescriptEdit it. ⌘/Ctrl + Enter runs.
You should see
Clicked: Save
Your turn
Add a second property, disabled: boolean, to the Button interface and log it alongside this.label.
Error you will hit

TS2684: a this-typed function detached from its receiver

typescript
interface Button {
  label: string
}

function sayLabel(this: Button) {
  console.log(this.label)
}

const button: Button = { label: "Save" }
const detached = sayLabel
detached()
app.ts:9:7 - error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Button'.
  Property 'label' is missing in type 'void' but required in type 'Button'.

9 const detached = sayLabel
        ~~~~~~~~
Why the compiler said that

sayLabel demands that whatever calls it supplies a this with a label property. Assigning it to a plain variable and calling it as detached() calls it with no meaningful this at all — exactly the runtime bug the this parameter exists to catch before it happens.

The fix

Call it in a way that supplies the right this — as a method on an object that has it, or explicitly with .call().

typescript
sayLabel.call(button)
this parameter
A fake first parameter named this in a function declaration that types what this must be inside the function body. Erased at compile time — never a real runtime argument.
SeniorSince a this parameter is erased and this is still dynamic at runtime in plain JavaScript, what bug class does typing this actually prevent?

It does not change JavaScript's runtime behavior at all — this is still resolved dynamically by how the function is called. What it prevents is exactly the class of bug that comes from that dynamism: a method being detached from its object (passed as a callback, stored in a variable, used as an event handler) and called with the wrong or missing this. The this parameter lets the compiler catch that mismatch at every call site, at compile time, instead of the bug only surfacing when that code path runs in production.

What they are really testing: Whether the candidate understands typing this is a compile-time safety net over a runtime behavior that TypeScript itself cannot change.

05

void vs never return types

void and never both describe functions with no useful return value, but they mean very different things. A function typed void does return — normally, to its caller — it just returns nothing meaningful (undefined, effectively). A function typed never never returns at all: it always throws an exception, or never terminates. The distinction matters for control flow analysis — code placed right after a call to a never-returning function is unreachable, which TypeScript can use to your advantage in exhaustiveness checks.

typescriptEdit it. ⌘/Ctrl + Enter runs.
You should see
Logged
Something broke
Your turn
Add a second console.log line right after fail("Something broke") inside the try block — reason about whether it would ever run.
voidnever
Does the function return?Yes, normally, with no useful valueNo — always throws or loops forever
What can you assign the result to?Only void or undefined-tolerant positionsA never value can be assigned anywhere — it is a subtype of every type
Typical useCallbacks, event handlers, functions run only for their side effectFunctions that always throw; the unreachable branch of an exhaustive switch
Quick check

Which return type would you give a function whose only job is to throw an error?

06

Function types as values

A function type describes the shape of a function — its parameter types and return type — independent of any particular implementation, written as (a: number, b: number) => number. Naming one with a type alias, like type Adder = (a: number, b: number) => number, lets you store functions in variables, pass them as arguments, and put them in arrays, all with the compiler checking every one against the same contract.

typescriptEdit it. ⌘/Ctrl + Enter runs.
You should see
8
2
14, 6
Your turn
Add a third function, multiply, matching the Adder shape, and push it into the ops array.
The parameters of add and subtract have no annotations — and that is contextual typing again
Because add and subtract are declared with the : Adder annotation, TypeScript already knows the expected shape and infers a and b as number inside each arrow function, the same mechanism from Module 02.
Function type
A type describing a function's parameters and return type, e.g. (a: number, b: number) => number, independent of any implementation.
First-class function
A function that can be stored in a variable, passed as an argument, and returned from another function — true of every JavaScript (and therefore TypeScript) function.
  • Callback parameters: function onDone(cb: () => void)
  • A strategy/lookup table: const handlers: Record<string, (e: Event) => void>
  • Higher-order functions: a function that takes or returns another function, typed end to end

Finish the TypeScript handbook, then get hired

Sit the exam for your certificate, run your resume through the ATS checker, and see the jobs that ask for exactly this.

Check my resume
Found this course useful? Share it.
ShareXLinkedIn

Comments

0

Join the conversation. Sign in to leave a comment — we'd love to hear your thoughts.