Let TypeScript Figure It Out
Inference
TypeScript infers types from values. The less you annotate, the more idiomatic your code looks.
What you'll learn
- Predict what TS will infer
- Recognize where inference works well
- Tell `let` from `const` for inference
TypeScript watches your values and infers their types. Most of the time, you don’t need annotations — the compiler already knows what you mean.
Variable Inference
let name = "Ada"; // string
let age = 36; // number
let admin = true; // boolean
let scores = [10, 9, 7]; // number[]
let user = { name: "Ada" }; // { name: string } Hover any of these in your editor — you’ll see the inferred type.
let vs const
let kind = "admin"; // string (mutable, so widens)
const kind = "admin"; // "admin" (immutable, literal type) const infers the narrowest type possible — the literal value
itself. let infers wider since you might reassign.
Return Types
TS infers what your function returns:
function add(a: number, b: number) {
return a + b;
}
// Inferred return type: number For local helpers, leave it inferred. For public APIs, annotate to make the contract explicit.
Contextual Typing
TS uses the context to type things:
const buttons = ["save", "cancel"];
buttons.forEach(b => {
console.log(b.toUpperCase()); // b is inferred as string
}); The callback’s b is inferred from buttons’ type — no annotation
needed.
Object Inference Widening
const user = { kind: "admin" };
// Inferred: { kind: string } — kind is widened to string Object fields default to widened types (string, not "admin").
If you need the literal type, use as const:
const user = { kind: "admin" } as const;
// Inferred: { readonly kind: "admin" } We cover as const properly in the assertions lesson.
When Inference Falls Short
A few places need help:
- Function parameters (TS can’t know callers)
- Empty arrays / null initial state
- Complex generic functions
- Library APIs without good types
A Working Style
- Default: don’t annotate locals or returns
- Always: annotate parameters
- Sometimes: annotate exported function returns for clarity
- As needed: annotate when inference produces something unexpected
A great test: delete an annotation. If TS keeps inferring the right thing, the annotation was redundant.
Up Next
Naming types so you can reuse them — type aliases.