Type Inference
TypeScript often just knows
You do not have to annotate everything
Good news: TypeScript is smart enough to figure out most types on its own. This is called type inference. When you initialize a variable, TypeScript looks at the value and infers the type without being told:
let city = 'Cardiff';
let population = 372000;
let isCapital = true;No annotations anywhere, yet city is a string, population is a number, and isCapital is a boolean. All the usual protection still applies. Write city = 42; later and you get the same error as if you had annotated it by hand. Hover over any variable in your editor and it will show you the inferred type.
Inference flows through your code
Inference is not limited to simple variables. Return types, array elements, and callback parameters are all inferred from context:
function double(n: number) {
return n * 2;
}
const nums = [1, 2, 3];
const bigger = nums.map((n) => n * 10);TypeScript infers that double returns a number, that nums is number[], and that n inside the map callback is a number. You wrote one annotation, on the parameter, and everything downstream came free.
So when should you annotate?
A practical rule of thumb that most teams follow:
- Always annotate function parameters. TypeScript cannot guess what callers will pass in, so this is where annotations matter most.
- Annotate empty containers, like
let items: string[] = [];, because an empty array gives inference nothing to work with. - Let initialized variables infer. Writing
let city: string = 'Cardiff';is not wrong, just noise. - Return types are your choice. Annotating them documents intent and catches mistakes inside the function; letting them infer keeps code shorter. Both styles are common.
A small twist with const
Declare a value with const and TypeScript may infer a literal type rather than a broad one:
const mode = 'dark';Since a const can never be reassigned, TypeScript infers the type 'dark', the exact literal, rather than string. This pairs beautifully with the literal unions from the last lesson, and it happens automatically.
Takeaway: inference means TypeScript gives you safety without drowning your code in annotations. Annotate the boundaries, especially function parameters, and let TypeScript handle the middle.
Quick check
Q1 What type does TypeScript infer for population in let population = 372000?
Q2 Where do annotations matter most, even though inference exists?
Want to save your progress? It's free.
Create a free account