TypeScript can infer most types without you writing them out — annotate return types and exported function signatures for documentation and safety, and let it infer the rest.
let count = 5; // inferred as number
let name = "Ada"; // inferred as string
let isDone = false; // inferred as boolean
const numbers = [1, 2, 3]; // inferred as number[]
const mixed = [1, "two", true]; // inferred as (string | number | boolean)[]
function double(n: number) { // parameters need explicit types — TS can't infer these
return n * 2; // return type inferred as number
}
Function parameters are the one place inference can't help you — TypeScript has no way to know what a caller intends to pass, so untyped parameters default to any (or raise an error, depending on your noImplicitAny setting — see the tsconfig section). Return types, local variables, and object literals are usually left to inference; explicit return type annotations are most valuable on public/exported functions, where they act as a contract and catch you accidentally returning the wrong thing.
// Contextual typing: TS infers the parameter type from where the function is used
const numbers2 = [1, 2, 3];
numbers2.map((n) => n * 2); // `n` is inferred as number, from Array<number>.map's own signature