Primitives and Type Inference
Learn how TypeScript understands primitive values and automatically infers types to make your code safer without requiring extra syntax.
We'll cover the following...
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.
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:0,42,3.14.boolean: This istrueorfalse.
When you assign one of these to a variable, TypeScript infers the type immediately:
Explanation:
Line 1:
useris inferred asstring.Line 2:
ageis inferred asnumber.Line 3:
isActiveis inferred asboolean.
This is inference in action: you write code the way you always have, and TypeScript quietly adds type safety behind the scenes.
Type inference is sticky
Once TypeScript infers a type, that type sticks. Try to change it later, and the compiler will stop you.
Explanation:
Line 1:
versionis inferred asnumber.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";
Explanation:
Line 1: Because
modeuseslet, TypeScript assumes it might change later—so it gives it the general typestring.Line 2: But because
themeis declared withconst, 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.
Explanation:
Lines 1–3:
datais declared with no initializer, so TypeScript assigns it the typeany.Lines 7–9:
resultis initialized withnull, 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
constfor 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
nullorundefinedas 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.
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—
string,number, andboolean—are inferred automatically.letinfers general types (likestring), whileconstcan 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 toany, 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!
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.