Functions
Typing functions, parameters and return values, optional/default/rest parameters, and function overloads.
6 questions
JuniorTheoryVery commonHow do you type a function's parameters and return value?
How do you type a function's parameters and return value?
Annotate each parameter as (name: T) and write the return type after the parentheses: function add(a: number, b: number): number. The return type can also be inferred from the body. Parameters may be optional with ? (after required ones), have a default via =, or be a rest parameter ...args: T[]. A standalone function type is (x: number) => string, and void marks a function that returns nothing.
Common mistakes
- ✗Putting an optional
?parameter before a required one — optional must come last - ✗Writing the rest parameter as
args...: Tinstead of...args: T[] - ✗Assuming every return type must be annotated when it is usually inferred
Follow-up questions
- →When is annotating the return type worth it despite inference?
- →What is the difference between an optional parameter and a default parameter?
JuniorTheoryCommonHow does an optional parameter ? differ from a parameter with a default value?
How does an optional parameter ? differ from a parameter with a default value?
Both let the caller omit the argument, but the type inside the body differs. ? widens the parameter to T | undefined, so you must check it before use. A default = v keeps the type T, because the value is substituted whenever the argument is undefined. The two cannot be combined on one parameter.
Common mistakes
- ✗Writing
f(x?: number = 0)—?and a default cannot be combined on one parameter - ✗Forgetting that
?types the parameterT | undefinedand using it without a check - ✗Believing a default fires for
null— it fires only forundefined
Follow-up questions
- →Why must an optional parameter follow every required one?
- →What happens when a caller explicitly passes
undefinedto a defaulted parameter?
MiddleTheoryCommonWhat are function overloads in TypeScript?
What are function overloads in TypeScript?
Function overloads are multiple call signatures declared above one implementation, letting a single function present different parameter and return combinations to callers. Only the overload signatures are visible to callers; the implementation signature — often using broader or any types — is not callable directly and must be compatible with every overload. Use overloads when the return type depends on the argument shape, though a union type is often simpler.
Common mistakes
- ✗Thinking the implementation signature is callable — only the overload signatures are visible to callers
- ✗Believing overloads affect runtime dispatch — they are erased and only guide the type checker
- ✗Reaching for overloads when a single union-typed parameter would be simpler
Follow-up questions
- →When should you prefer a union type over overloads?
- →Why must the implementation signature be compatible with every overload?
MiddleCodeOccasionalType a debounce wrapper that preserves the wrapped function's parameters
Type a debounce wrapper that preserves the wrapped function's parameters
Make the wrapper generic over the function type, <T extends (...args: any[]) => void>, and type the returned function (...args: Parameters<T>) => void. The caller gets the wrapped parameters checked at every call, not an any[].
Common mistakes
- ✗Typing the wrapped function as
Function, which drops every parameter check - ✗Returning
(...args: any[]) => void, so the wrapper's caller loses the original arity - ✗Forgetting that a debounced call cannot return the wrapped function's result
Follow-up questions
- →How would you also preserve the receiver with
ThisParameterType<T>? - →What changes if the debounced call must return a
Promiseof the result?
MiddleTheoryOccasionalWhen is an overload set better than one signature with union-typed parameters?
When is an overload set better than one signature with union-typed parameters?
Use overloads when the return type follows the argument: parse(s: string): Node, parse(s: string[]): Node[]. A union signature returns Node | Node[] and forces the caller to narrow. If every input yields one type, a union signature is simpler.
Common mistakes
- ✗Writing an overload set where every signature returns the same type
- ✗Expecting a union signature to give the caller a precise return type per input
- ✗Forgetting the implementation signature is not callable and must cover every overload
Follow-up questions
- →How would a generic parameter with a conditional return type replace the overloads?
- →Why can the implementation signature not be called directly?
MiddleTheoryOccasionalWhat does declaring this as the first parameter of a function do?
What does declaring this as the first parameter of a function do?
It is a fake parameter that types the receiver: function onClick(this: HTMLElement, e: Event). The compiler rejects a call whose this does not match, and strictBindCallApply extends the check to bind, call and apply. It is erased at emit.
Common mistakes
- ✗Thinking callers have to pass an extra argument for the
thisparameter - ✗Trying to declare a
thisparameter on an arrow function - ✗Expecting the annotation to bind the receiver at run time rather than only check it
Follow-up questions
- →What does the compiler flag
noImplicitThisadd on top of this annotation? - →How does
ThisParameterType<T>let a wrapper forward the receiver?