Inference

Let TypeScript Figure It Out

Inference

TypeScript infers types from values. The less you annotate, the more idiomatic your code looks.

4 min read Level 1/5 #typescript#inference#types
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.

Type Aliases →