Skip to Content
TypeScript for Beginners: Catch Wrong Data Before It Runs
Beginner
0 likes
Courses
TypeScript for Beginners: Catch Wrong Data Before It Runs

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.

Course Home

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 Number or String with 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 null or undefined to 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
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.