...

/

Primitives and Type Inference

Primitives and Type Inference

Learn how TypeScript understands primitive values and automatically infers types to make your code safer without requiring extra syntax.

Let’s clarify immediately: TypeScript isn’t about forcing you to add types to everything. One of the most powerful things it does is infer types for you automatically and behind the scenes without getting in your way. That’s called type inference, one of TypeScript’s superpowers.

When we write let score = 100;, TypeScript sees the value and says, “Got it. That’s a number.” No annotation is needed. Clean, readable, and safe.

Press + to interact
Type inference in TypeScript
Type inference in TypeScript

But inference isn’t foolproof. Sometimes, it falls short—especially if we don’t give TypeScript enough information up front. And when that happens, our code can become as fragile as plain JavaScript.

This lesson is about learning how TypeScript thinks—how it sees the values we write, how it infers their types, and when it needs our help. Understanding this will make you faster, safer, and sharper in every line you write.

The primitive types

Let’s start with the foundation. TypeScript builds its type system on top of JavaScript’s values, and that begins with primitive types.

Here are the ones we’ll focus on now:

  • string: This is for text values like "Ada" or "typescript".

  • number: This is for whole numbers and decimals: 0423.14.

  • boolean: This is true or false.

When you assign one of these to a variable, TypeScript infers the type immediately:

Press + to interact
TypeScript 5.8.3
Files
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true
}
}

Explanation:

  • Line 1: user is inferred as string.

  • Line 2: age is inferred as number.

  • Line 3: isActive is inferred as boolean.

This is inference in action: you write code the way you always have, and TypeScript quietly adds type safety behind the scenes.

Hover over variables to see their inferred types.

Type inference is sticky

Once TypeScript infers a type, that type sticks. Try to change it later, and the compiler will stop you.

Press + to interact
TypeScript 5.8.3
Files
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true
}
}

Explanation:

  • Line 1: version is inferred as number.

  • Line 2: We try to assign a string—TypeScript throws a compile time error.

TypeScript isn’t just watching the value you write—it’s locking in a rule: this variable will always be a number. Break that rule later, and TypeScript steps in.

The let vs. the const: General vs. specific types

Now here’s something more subtle. TypeScript not only looks at the value—it also pays attention to whether or not the value can change.

let mode = "dark";
const theme = "dark";
let infers a general string; const infers the specific value "dark"—a string literal

Explanation:

  • Line 1: Because mode uses let, TypeScript assumes it might change later—so it gives it the general type string.

  • Line 2: But because theme is declared with const, TypeScript knows it will never change—and infers a more specific type based on the exact value.

This might seem like a small detail, but it plays a big role later when we want to restrict or validate specific values. We’ll dive deeper into that soon.

Whenever possible, prefer const. It gives TypeScript stronger guarantees to work with.

What happens when TypeScript can’t infer safely?

TypeScript’s type inference is only as good as the information we give it. It can infer confidently when we assign a clear value—like a string or number. But if we leave a variable empty or give it something vague like undefined or null, inference falls apart. In these cases, it falls back to a special type called any, which disables type checking for that variable entirely.

Press + to interact
TypeScript 5.8.3
Files
let data;
data = "hello";
data = 42;
console.log(`Data is: ${data}`);
let result = null;
result = "done";
result = true;
console.log(`Result is: ${result}`);

Explanation:

  • Lines 1–3: data is declared with no initializer, so TypeScript assigns it the type any.

  • Lines 7–9: result is initialized with null, which also gives TypeScript nothing specific to work with. The result is the same: any, and no type checking.

When we don’t tell TypeScript what type a variable should hold, it can’t infer meaningfully, and we lose all the benefits of static typing.

Note: any is a complete escape hatch. It opts you out of the type system. If you see any, you’re not writing safe TypeScript—you’re writing untyped JavaScript again.

Best practices for type inference

TypeScript’s inference engine is smart—but only if we help it. Here’s how to keep it on your side:

  • Initialize variables when you declare them. That’s when TypeScript infers the type.

  • Use const for values that never change. It produces more specific (and safer) types.

  • Avoid declaring variables without initial values. TypeScript will fall back to any, and you’ll lose type safety.

  • Don’t use null or undefined as placeholders. They don’t tell TypeScript what kind of value the variable should eventually hold.

This flowchart sums up the general pattern of how TypeScript assigns types during variable declaration based on what we’ve seen so far.

Press + to interact
How TypeScript infers types
How TypeScript infers types

When you write code that TypeScript can understand clearly, you get the full benefit of static types—without writing a single annotation.

Key takeaways:

  • TypeScript infers types based on the value assigned at initialization.

  • The core primitive types—stringnumber, and boolean—are inferred automatically.

  • let infers general types (like string), while const can infer specific value-based types (like "dark").

  • If you don’t assign a meaningful initial value like when using null, undefined, or leaving it empty—TypeScript falls back to any, which disables type safety for that variable.

Exercise: Preserve intent through inference

The following code looks fine but one variable is declared without a value, and another is initialized with null. Both end up with unsafe types. Your task here is to change the declarations so each variable lands on the intended safe type.

Good luck!

Press + to interact
TypeScript 5.8.3
Files
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true
}
}

Try solving the problem on your own first. If you’re feeling stuck, you can click “Show Hints” for guidance or “Show Solution” to view the complete answer.

Access this course and 1200+ top-rated courses and projects.