How Type Inference Works
New TypeScript users often annotate everything: let count: number = 0, const title: string = "Buy milk". It works, but it is noise. The compiler can already see those types. Knowing what it infers tells you exactly where annotations earn their place.
The rule
Where there is a value, TypeScript infers; where there is none, you annotate.
Inference means the compiler works out a type from the code around it.
let count = 0; // number
const status = "open"; // "open" (a literal type)
let tags = ["home", 1]; // (string | number)[]
function makeTask(title: string) {
return { title, done: false };
}
// return type inferred: { title: string; done: boolean }Notice the difference between let and const. A let variable can be reassigned, so TypeScript widens 0 to the general type number. A const can never change, so it keeps the exact literal type "open". For an array of mixed values it infers an array of a union — "each element is a string or a number".
Inference also flows into callbacks. This is called contextual typing:
const ids = [3, 1, 2];
ids.map(n => n * 2); // n is number, because ids is number[]The place inference cannot help is a function parameter with no default value. Nothing in the function tells TypeScript what callers will pass, so title would silently become any. With the noImplicitAny check (part of strict mode) that is an error, which is why parameters are the main thing you annotate.
The shortcut
Scan a line and ask "Can the compiler see a value here?"
- A variable with an initial value → it can see one. Skip the annotation.
- A function's return → it can see the
returnstatements. Annotation optional. - A parameter, or a variable declared with no value → it cannot. Annotate.
let nextId = 1;Value visible, so
nextIdis inferred asnumber. No annotation needed.
A common surprise: let status = "open" has type string, not "open", so it will not fit a parameter that only accepts "open" | "closed". Use const or an annotation when you need the literal.
Drills
Answer fast — trust the rule.
What type does TypeScript infer for let price = 9.5;?
What type does TypeScript infer for const mode = "dark";?
Where does TypeScript usually need you to write a type annotation?
In ["a", "b"].map(s => s.length), what is the type of s?
What does TypeScript infer for let mixed = [1, "two"];?
Remember
- Where there is a value, TypeScript infers; where there is none, you annotate.
letwidens tonumberorstring;constkeeps the literal.- Trap: unannotated parameters become
any— strict mode turns that into an error.
