Functions and generics
Use inferred and explicit return types
Let simple implementation details infer their result and annotate public boundaries when the promised return type matters.
TypeScript infers a return type from every return path. You rarely have to write one, and for small local functions inference is often enough.
For example, this result is inferred as number:
function total(values: number[]) {
return values.reduce((sum, value) => sum + value, 0)
}
Hover over total in your editor and you see (values: number[]) => number. The compiler worked that out from the reduce() call and its 0 seed.
When to annotate
An explicit return annotation is useful when the function promises a public contract:
function total(values: number[]): number {
return values.reduce((sum, value) => sum + value, 0)
}
The difference shows when the implementation changes. Suppose someone edits the function to return values.length ? sum : 'none'. Without the annotation, the inferred type quietly becomes string | number, and the errors appear in every caller that does arithmetic with the result. With : number, the error appears inside the function, at the bad return statement:
Type 'string' is not assignable to type 'number'.
Now an accidental string return fails inside the function instead of changing the type seen by every caller. The person who made the change sees the error, in the file they were editing. That locality is the main argument for annotating exported functions.
Annotations also help recursive functions and functions with several branches. A recursive function sometimes cannot infer its own result. Multiple branches make it easy to miss a path that returns undefined. Writing the return type makes you decide whether every path returns a value.
When not to annotate
Do not annotate every small local function. Inference keeps implementation code readable and often preserves a more precise type — a function returning 'asc' infers the literal type 'asc', while a hasty : string annotation would throw that precision away.
My rule: annotate the exported surface of a module, let the internals infer.
Exercise: add an empty-array branch returning 'none'. Compare the inferred return type with the error produced by the explicit : number contract.
Lesson completed