Annotating Variables Without Fighting the Compiler
Annotate variables, let inference do the rest, and handle null and the name clash that trips up new files. After this, you know when a type belongs in your code and when it is noise.
Course content
Sign in to save your progress.
-
Before You Write Types
-
What TypeScript Adds on Top of JavaScript
-
Install TypeScript and Run Your First .ts File
-
-
-
Types You Cannot Skip
-
Annotating Variables Without Fighting the Compiler
-
Arrays, Objects and Nested Shapes
-
Optional, Readonly and Why any Is a Trap
-
-
-
Functions That State Their Deal
-
Parameter and Return Types
-
Optional and Default Parameters Without Surprises
-
-
-
Shapes You Can Reuse
-
Interfaces vs type Aliases: Pick One and Stick With It
-
Unions, Literals and Narrowing With typeof and in
-
-
-
Compile Without the Mystery
-
tsconfig.json: target, module and strict
-
Reading tsc Errors Without Panicking
-
-
- Final Check: TypeScript for Beginners
Skip ahead: If you already know the difference between const and let inference and why strictNullChecks rejects null, skip to "When you must write the type", then continue.
An annotation is the colon and type you put after a name: let price: number. It tells the compiler what the variable is allowed to hold. Write too few and the compiler guesses. Write too many and your code turns into noise. Most of the fighting beginners do comes from not knowing which one the situation needs.
The basic annotations
let price: number = 100;
let title: string = "Notebook";
let inStock: boolean = true;
price = "free";
inStock = 1;
basic.ts(5,1): error TS2322: Type 'string' is not assignable to type 'number'.
basic.ts(6,1): error TS2322: Type 'number' is not assignable to type 'boolean'.
Three primitive types cover most of what you write for now: number, string and boolean. Use the lowercase names. TypeScript also has Number and String, which describe the wrapper objects from JavaScript. You almost never want those.
Both errors have the same shape: Type 'X' is not assignable to type 'Y'. The first type is what you tried to put in. The second is what the variable was declared to hold. Read it in that order and the message makes sense.
Let the compiler infer
You do not have to write the type when you give the variable a value on the same line.
let count = 0;
count = "zero";
const city = "Manila";
let place = city;
place = "Cebu";
infer.ts(2,1): error TS2322: Type 'string' is not assignable to type 'number'.
let count = 0 has no annotation, yet the compiler decided it is a number and rejected the string. That is inference. Hover over a variable in your editor to see what it decided.
The const case is more precise. city can never change, so its type is the exact text "Manila", not just string. When it is copied into place, which is a let, the type widens to string, so assigning "Cebu" is fine. You will use that exact-value idea again with literal types in a later lesson.
const total = 5 * 12;
const label = "Total: " + total;
console.log(label);
Writing const total: number = 5 * 12 here adds nothing. The compiler already knows. My rule: if the right-hand side makes the type obvious, leave the annotation off.
When you must write the type
There are cases where the compiler cannot know, and you have to tell it.
let discount: number;
let note: string = null;
discount = 0.1;
console.log(discount, note);
declared.ts(2,5): error TS2322: Type 'null' is not assignable to type 'string'.
let discount: number; declares a variable without a value. With no value, there is nothing to infer from, so you state the type. This is the normal case for annotations.
The second error matters more. Under strict, which is on by default when you run tsc on a file with no tsconfig.json, null is not a string, so let note: string = null fails. In plain JavaScript, null slides into anything, and the crash happens later when you call a method on it. TypeScript moves that failure to the line where the mistake is.
⚠ Warning: Do not fix a null error by changing the type to any. You turn off checking for that variable, and you lose the reason you installed the compiler.
A name that is already taken
One error trips up almost everyone in the first week.
let name: string = "Ana";
console.log(name);
taken.ts(1,5): error TS2451: Cannot redeclare block-scoped variable 'name'.
You never declared name twice, so why the complaint? A .ts file with no import or export is treated as a script, and a script shares one global scope with TypeScript's built-in browser definitions. Those already declare name, status, length and a few others. tsc prints a second line that points into a lib.dom.d.ts file, and the line in your own file is the one to read.
The fix is to make the file a module. Add this line at the top and the error goes away.
export {};
let name: string = "Ana";
console.log(name);
Or pick a name that is not taken. Inside a function, a parameter called name is fine, as in greet from the last lesson.
Common mistakes
- Annotating everything, including values the compiler already knows. It is harmless but it hides the places where an annotation really matters.
- Using
NumberorStringwith a capital letter. - Leaving the type off a function parameter. The compiler has nothing to infer from, and strict mode reports
Parameter 'x' implicitly has an 'any' type. - Assigning
nullorundefinedto a typed variable and then asking why strict mode complains. It is complaining about a real crash.
💡 Tip: Annotate function parameters and variables declared without a value. Let inference handle the rest. Next lessons build on that.
If you skip this and annotate by habit, you will end up with code that reads like a form to fill in, and you will still not know what the compiler infers. Do you know what type it chose for the last variable you wrote?
About
Annotate variables, let inference do the rest, and handle null and the name clash that trips up new files. After this, you know when a type belongs in your code and when it is noise.
- Duration
- 5 min
- Last update
- 10/06/2026
Comments (0)
Sign in to commentNo comments yet.