Type Annotations & Inference
The primitive types, when to annotate explicitly, and how TypeScript infers types you never wrote.
The syntax for a type annotation is a colon after the name:
let username: string = "ada";
let age: number = 36;
let isAdmin: boolean = false;
let ids: number[] = [1, 2, 3];But here's the thing — you rarely need to write these. TypeScript reads the value on the right and figures the type out itself:
let username = "ada"; // inferred: string
let age = 36; // inferred: number
let ids = [1, 2, 3]; // inferred: number[]
username = 42; // ❌ Type 'number' is not assignable to type 'string'This is type inference, and good TypeScript leans on it heavily.
What is the inferred type of count in let count = 10;?
- Aany
- Bnumber
- C10
- Dstring
const infers differently than let — and the difference matters:
let status1 = "active"; // type: string (can be reassigned)
const status2 = "active"; // type: "active" (the literal!)Because a const primitive can never change, TypeScript infers the literal type — the exact value as a type. status2 isn't just some string; it is forever the string "active", and its type says so.
The flip side has a name worth knowing now, because it comes up a lot: when let status1 = "active" becomes the general string instead of the exact "active", we say the type widens. "Widening" is just TypeScript broadening a specific type into a more general one — it happens with let because the variable could be reassigned to any other string later. Every time you read widen in this course, that's all it means.
Literal types look odd at first, but they're the foundation of one of TypeScript's most powerful patterns (discriminated unions, coming in Module 3).
What type does TypeScript infer for direction?
const direction = "north";- Astring
- B"north"
- Cany
- Dobject
So when SHOULD you write annotations? Three places:
// 1. Function parameters — inference can't guess what callers will pass
function applyDiscount(price: number, percent: number) {
return price * (1 - percent / 100); // return type inferred: number
}
// 2. Variables declared before they're assigned
let retries: number;
retries = 3;
// 3. When you want a WIDER type than the value suggests
let route: string = "/home"; // not just "/home" — any string laterThe community rule of thumb: annotate function boundaries, infer everything else. Annotating const x: number = 5 adds noise, not safety.
- Parameters are the one place inference genuinely can't help you
Complete the annotation so the function only accepts numbers:
function square(n: _____) {
return n * n;
}Where do experienced TypeScript developers usually write explicit type annotations?
- AOn every variable, to be as explicit as possible
- BOn function parameters and public signatures; local variables are left to inference
- CNowhere — inference handles everything including parameters
- DOnly on variables that hold strings