AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 02 — Type Inference: Letting the Compiler Fill in the Types

02 — Type Inference: Letting the Compiler Fill in the Types

August 13, 20265 min read
Download as Markdown

"TypeScript just guesses the type" was my inference model, and it made the compiler feel unreliable. Writing it down corrected the framing: type inference is the compiler deriving a type from how a value is created and used, so I only annotate where the derivation isn't obvious. [1] It's not guessing; it's a deterministic walk over the code's structure.

The payoff is brevity without losing safety. The canonical example:

let count = 0;        // inferred as number
let name = "Ave"; // inferred as string

I wrote no annotations, yet count is locked to number and name to string. If I later assign count = "hello", the compiler objects — the inferred type is real. The rule the compiler follows is straightforward: look at the initial value, and pick the type that value has [1]. Annotation becomes a choice I make when the initial value isn't enough — empty variables, function parameters, or where I want to narrow what inference would otherwise allow.

Where inference does the work

A few places where inference quietly carries the load, and where it pays to know the rules:

  • Variable initialization. const x = 42 infers 42 as a literal type when declared const, but let x = 42 widens to number. The const vs let distinction shows up in literal types and as const later.
  • Function returns. If a function returns a + b where both are number, the return type is inferred as number without me writing it.
  • Destructuring. const { name } = user infers name's type from the shape of user.

The blind spot is function parameters. function add(a, b) infers any for a and b unless I annotate them or turn on noImplicitAny. That's why function signatures are where annotations cluster — inference won't save me there, and that's by design.

tsconfig.json: the project's compile contract

Inference doesn't run in a vacuum — it follows rules set in tsconfig.json, the declarative config file that lives at the project root [2]. This file is the single source of truth for how the compiler treats the whole project: which files to include, what JavaScript to emit, how strict to be. It's what makes a TypeScript build reproducible across machines and CI runs.

The structure is a set of nested options. A minimal sketch:

{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"strict": true,
"outDir": "./dist"
},
"include": ["src"]
}

That snippet already encodes real decisions: target ES2022 (so tsc doesn't down-convert modern syntax), emit ESNext modules, turn every strict check on, write output to dist, and only compile files under src. Every choice here changes what the compiler does to my code.

Compiler options: the levers

The compilerOptions block is where the project's personality is set [3]. The options I actually reach for:

  • target — which JavaScript version to emit. Lower targets mean more down-conversion; ESNext means "leave my syntax alone."
  • module — the module system in the output (CommonJS for Node, ESNext/Preserve for bundlers).
  • strict — a master switch that enables a family of strict checks at once. This is the single most important option.
  • strictNullChecks — under strict; forces me to handle null and undefined explicitly instead of letting them sneak into every type.
  • noImplicitAny — under strict; errors when a type would silently become any, which is what makes inference trustworthy on parameters.
  • outDir / rootDir — where compiled JavaScript goes, and where the source lives.
  • sourceMap — emit source maps so stack traces point at .ts files, not the compiled .js.

The pattern I've settled on: start a new project with "strict": true and never look back. The friction of fixing the initial wave of errors is far smaller than the bugs that strict mode prevents over the project's life. Inference plus strict mode together are what make TypeScript feel lightweight — I write little explicit syntax but get strong guarantees.

How inference interacts with strict mode

The link worth stating plainly: strict mode is what makes inference safe to lean on. With noImplicitAny off, the compiler will happily infer any for an untyped parameter and then refuse to flag anything done with it — inference becomes a hole. With it on, the compiler insists on a real type, either inferred from context or annotated by me. The result is that the inferred types are actually trustworthy, and I can let the compiler do the work without surrendering safety.

How I use this

My rule of thumb for annotations: annotate function signatures (parameters and return type when it's non-obvious or part of a public API), and let inference handle everything inside function bodies. I resist the urge to annotate every local variable — it's noise, and the inferred type is already correct. When I'm unsure what type was inferred, I hover in the editor and read it; if it's wider than I want, that's the moment to annotate or reach for as const. And the very first thing I set in any tsconfig.json is "strict": true — it's the switch that makes all the other inference behavior worth trusting.

References

[1] Microsoft, "Type Inference," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/type-inference.html#handbook-content

[2] Microsoft, "What is a tsconfig.json," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#handbook-content

[3] Microsoft, "Compiler Options," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/compiler-options.html

[4] The New Stack, "TypeScript Getting Started," 2024. [Online]. Available: https://thenewstack.io/typescript-tutorial-a-guide-to-using-the-programming-language/

Knowledge check · Question 1 of 5

`let count = 0;` — what is the inferred type of `count`?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!