1let count = 3;2let label = "apples";3let total = count * 2;4const unit = "kg";5count = "three";count = 4;tsctype checker!✓count?numberlabel?stringtotal?numberunit?"kg"let count: number3"apples"count * 2"kg""three"4numbernumberstring"kg"
Back row: infer.ts (no type annotations) and tsc, the type checkerFront row: the type tsc inferred for each nameTokens: a value going into tsc, and the type coming outAt the back: infer.ts, five lines with no types written, and tsc, the type checker. In front: the types tsc works out, one cell per name. Each starting value hops into tsc and a type comes out: count and total are numbers, label is a string, and the const unit gets the literal type "kg". On line 5 the inferred type catches count = "three" before anything runs. tsc also infers what a function returns from its return statements, so function half(n: number) { return n / 2; } returns number without you writing it.

No types written

  1. Look at this file: not one type is written. Yet TypeScript knows what kind of value every variable holds. It works the types out from the code itself. That is called type inference.

Inferring each line

A value that does not fit

In your editor

What happens

;; step 1/9 · no-types
infer.ts
  1  let count = 3;
  2  let label = "apples";
  3  let total = count * 2;
  4  const unit = "kg";
  5  count = "three";

no  : number   no  : string   anywhere
tsc will work every type out by itself

A function parameter with no annotation is not inferred from how you call the function: it becomes any, which the noImplicitAny setting (on in strict mode) reports as an error. The exception is contextual typing, such as a callback passed to forEach, where TypeScript knows the type from where the function goes. The error text, the four inferred types and the hover text were checked with TypeScript 5.9 (tsc and its checker) on this exact file.