Skip to content

TypeScript lesson 1 of 9

TypeScript: Checked, Then Run

What actually happens when TypeScript runs your code - a compiler checks the whole program against the rules of every value's type, and only a program with no errors ever reaches console.log.

Published · Every example on this page was run before it was published.

JavaScript runs the instant you hand it to an engine: line one can start executing while line ten has not even been read yet. TypeScript adds a step in front of that. Before a single line of your program runs, a separate program - the compiler - reads the whole file and checks whether every value is being used in a way that makes sense for its type: the kind of thing it is, and what you are allowed to do with it. Only once that check passes does anything actually run. This lesson is about that check - what it is checking, what it looks like when it fails, and why a language would bother adding it at all.

Two Phases: Check, Then Run

Every time you press Run, two completely separate things happen, in order. First, the compiler reads your entire file and asks, line by line, "does every operation here make sense for the types of value involved?" It does this without running anything - no console.log has fired, no variable has been created, nothing exists yet except the text of your program. If it finds even one line that does not make sense, it stops right there and reports every problem it found. Nothing runs. Second, and only if the first phase found nothing wrong, your program actually executes, top to bottom, exactly the way a plain JavaScript file would.

That ordering matters more than it looks like it should. A mistake on the very last line of a hundred-line program is caught before the first line ever runs, because the whole file is checked as a unit before any of it is treated as instructions to carry out.

console.log: Your Window Into a Running Program

Once a program passes the check and runs, console.log is how it talks to you. Whatever you pass between its parentheses is written to the output panel, and you can pass more than one value at a time, separated by commas - each one gets printed with a single space between them.

TypeScript
console.log("Booking confirmed");
console.log("Seat:", "14A");
console.log("Price:", 249.99);
Output
Booking confirmed
Seat: 14A
Price: 249.99

Three calls, three lines. The second and third calls each pass two values, and console.log joins them with one space rather than requiring you to build the combined string yourself.

What "Type Checking" Actually Means

Every value in your program has a type - a description of what kind of thing it is and, from that, what operations are sensible to perform on it. The compiler tracks the type of every value as it reads your file, and for every operation you write, it asks: does this operation make sense for a value of this type? Multiplying two numbers makes sense. Multiplying a piece of text by a number does not - and unlike JavaScript, TypeScript refuses to guess what you meant.

TypeScript
const price = "20";
const total = price * 2;
console.log(total);
Output
main.ts(2,15): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

Nothing ran here - not even the first line's assignment, because the whole file is checked before any of it starts. The compiler looked at price, saw that its type is string (because "20" is text between quotes, not a number), and refused to let it take part in multiplication. Compare that with plain JavaScript, where "20" * 2 runs perfectly fine and quietly produces 40, silently converting the string to a number on your behalf. TypeScript's position is that this kind of silent conversion is exactly the sort of thing that should stop your program with a clear message, not something you discover later when a number on a receipt is wrong. The fix is to say explicitly what you mean:

TypeScript
const price = "20";
const total = Number(price) * 2;
console.log(total);
Output
40

Number(price) converts the text "20" into the actual number 20, and only then does the multiplication make sense to the compiler.

A Second Kind of Check: Names That Do Not Exist

Type checking is not only about mismatched operations. The compiler also checks that every name you use - every variable, every function - was actually created somewhere before you tried to use it. A typo is the most common way to trip this:

TypeScript
const total = 12 + 30;
console.log(totall);
Output
main.ts(2,13): error TS2552: Cannot find name 'totall'. Did you mean 'total'?

total was created on line 1, but totall never was - it is a different name, spelled almost the same way. The compiler has no idea what you meant by it, only that nothing by that exact spelling exists. When the extra letter makes the misspelling close enough, the compiler even guesses the name you probably meant, the way it does here.

A Worked Example

Here is a small, complete program that only uses ideas you have already seen: several console.log calls, one on each line, printing a mix of text and numbers.

TypeScript
const eventName = "Jazz Night";
const ticketPrice = 45;
const ticketCount = 3;
const total = ticketPrice * ticketCount;
const soldOut = false;

console.log("Event:", eventName);
console.log("Tickets:", ticketCount);
console.log("Total due:", total);
console.log("Sold out:", soldOut);
Output
Event: Jazz Night
Tickets: 3
Total due: 135
Sold out: false

Every one of those five const lines was checked by the compiler before any console.log produced a single character of output. eventName was checked to see it is a valid string, total was checked to see that multiplying two numbers is sensible, and only after all five lines passed did execution begin and the four console.log calls run in order, top to bottom, exactly as written.

Common Mistakes

Expecting the lines before a mistake to print anyway. Because the whole program is checked before any of it runs, an error anywhere in the file - even on the very last line - means nothing runs, not even the console.log calls that come before it. A message where output should be means the compiler found a problem, not that everything above the mistake already ran and only the broken part failed.

Assuming TypeScript will "fix" a wrong-type operation the way JavaScript does. JavaScript converts values for you where it can, silently: "20" * 2 becomes 40 without complaint. TypeScript treats that same line as an error, on the theory that a program which runs the wrong operation without telling you is worse than a program that refuses to run at all until you say what you meant.

Confusing a type error with a runtime problem the type checker was never going to catch. Type checking asks whether an operation is sensible for the kinds of value involved - it does not ask whether the result is the one you wanted. Dividing by zero, for instance, is completely sensible as far as the type checker is concerned, because the answer is still a number:

TypeScript
console.log(10 / 0);
Output
Infinity

That is a real, if surprising, result of dividing by zero - not a compiler error, and not a crash. The type checker only rules out operations that do not make sense for the types involved; it has nothing to say about whether ten divided by zero is the number you actually wanted.

Expecting a type annotation to show up in the output. A type annotation exists purely for the compiler. Once your program passes its check, the annotations play no further role - they never appear in anything console.log prints, and they add nothing to what actually runs. The next lesson looks at exactly what those annotations are made of.

Next Steps

The linked practice problem, two-number-sum, is a good first target once you are comfortable with this idea: it is small enough that any type mistake you make will be easy to trace back to the exact line the compiler points at.

Before that, spend a few minutes in the TypeScript playground doing exactly what got you the two errors above, on purpose. Multiply a string by a number and read the exact wording the compiler gives you. Misspell a variable name you just declared and watch the compiler suggest the one you meant. Getting comfortable reading main.ts(line,column): error TSxxxx: ... now will save you time on every lesson that follows.

When you want a harder one

Interview-style problems graded against hidden tests — a big step up from the exercises. Come back to these when the ideas in this lesson feel comfortable rather than new.