Function Types and Callbacks
Functions are values in JavaScript — they are passed as arguments, returned from other functions, and stored in variables. TypeScript lets you type functions as values just as precisely as any other type.
Function type expressions
Section titled “Function type expressions”A function type describes a callable value: its parameters and return type:
type Formatter = (score: number, total: number) => string;Formatter is a type alias for any function that accepts a number and a number and returns a string. You can use it to type a variable holding a function:
const formatScore: Formatter = (score, total) => `${score} / ${total} correct`;Notice that the parameter names in the arrow function do not need to be score and total — TypeScript checks types and order, not names. The function satisfies Formatter as long as it takes two numbers and returns a string.
Callback parameters
Section titled “Callback parameters”When a function accepts a callback, you annotate the callback’s type inline or by reference:
function withResult( score: number, total: number, onComplete: (score: number, total: number) => void,): void { onComplete(score, total);}
withResult(7, 10, (s, t) => console.log(`${s} / ${t}`)); // ✓TypeScript infers the types of s and t from the onComplete parameter’s type — you do not need to annotate them at the call site. This is contextual typing: the expected type at a given position provides the type for the value passed there.
Contextual typing in array methods
Section titled “Contextual typing in array methods”You see contextual typing every time you use map, filter, or forEach:
const questions: TriviaQuestion[] = [];
questions.forEach(q => { console.log(q.question); // TypeScript knows q is TriviaQuestion});
const texts: string[] = questions.map(q => q.question); // inferred: string[]The callback passed to forEach has its parameter typed as TriviaQuestion because TypeScript knows questions is TriviaQuestion[]. You get full autocomplete and type checking inside the callback without writing a single annotation.
The difference between void and undefined
Section titled “The difference between void and undefined”A function typed to return void is not the same as one typed to return undefined:
type VoidFn = () => void;type UndefFn = () => undefined;
const a: VoidFn = () => 42; // ✓ — void callbacks may return any value; it is ignoredconst b: UndefFn = () => 42; // Error — must return undefinedconst c: UndefFn = () => undefined; // ✓This matters for callbacks. Array methods like forEach type their callbacks as returning void — meaning they do not care what the callback returns and will ignore it. If you accidentally write a map when you meant forEach, the return value would be discarded, but TypeScript would not complain because void accepts any return.
Typing the DOM event handler pattern
Section titled “Typing the DOM event handler pattern”AceIt’s main.ts attaches event listeners with callbacks. TypeScript knows the types of browser events:
const startBtn = document.getElementById('start-btn') as HTMLButtonElement;
startBtn.addEventListener('click', (event: MouseEvent) => { event.preventDefault(); // MouseEvent has preventDefault // ...});addEventListener is typed in the DOM library — it knows that 'click' events produce MouseEvent values. You rarely need to annotate event explicitly; TypeScript infers it from the event name.
Storing a typed function reference
Section titled “Storing a typed function reference”In main.ts, the answer-click handler uses a delegated pattern on a container element. Storing a handler reference with a typed variable makes the intent clear:
type AnswerHandler = (event: MouseEvent) => void;
const handleAnswerClick: AnswerHandler = event => { const btn = (event.target as HTMLElement).closest<HTMLButtonElement>('.answer-btn'); if (!btn || awaitingNext) return; // ...};
answersEl.addEventListener('click', handleAnswerClick);The type alias documents what the handler is, makes it reusable, and lets TypeScript verify the signature matches what addEventListener expects.
Exercise
Section titled “Exercise”In a new file handlers.ts (you can delete it after the exercise):
- Declare
type ScoreCallback = (score: number, total: number) => void. - Write a function
runQuiz(questions: TriviaQuestion[], onComplete: ScoreCallback): void— it should callonCompletewith a score and total after “completing” the quiz (simulate with hardcoded values). - Call
runQuizwith an inline arrow function. Confirm TypeScript infers the callback parameter types. - Write
function applyToAll(questions: TriviaQuestion[], transform: (q: TriviaQuestion) => string): string[]. Call it with a callback that returnsq.question. Confirm the return type isstring[].
- Function type expressions describe callable values:
(param: Type) => ReturnType. - Callback parameters are typed with function type expressions, either inline or via a
typealias. - Contextual typing lets TypeScript infer callback parameter types from the expected type — no annotation needed at the call site.
- Array method callbacks (
forEach,map,filter) get their parameter types from the array’s element type. - DOM event listener callbacks get their
eventparameter type from the event name —'click'infersMouseEvent.