Skip to content
aviral gupta

// B1.1 · ~30 min · Beginner

Why static types: catching mistakes before the program runs

After this lesson you can say what tsc checks before a program runs, add type annotations where they are needed, and use a diagnostic such as error TS2322 to fix the line it names.

Lesson 1 of 5 in B1 Setup and the basics

Start of the module

You will be able to

  • Explain what tsc checks before a program runs, and why node main.ts alone checks no types
  • Annotate variables and parameters, and leave annotations out where TypeScript infers the type
  • Read a tsc diagnostic: the file, line and column, the TS code and the message
  1. Warm-up · Activity 1 of 7

    Warm-up from JavaScript: which of these are true of a plain JavaScript program? Pick all that apply.

    Select all that apply.

  2. Predict · Activity 2 of 7

    Predict before you read on: you run npx tsc on this main.ts. What does it report?

    function double(n: number) {
      return n * 2;
    }
    
    console.log(double("21"));
  3. Practice · Activity 3 of 7

    Fill in the type of width, so that tsc accepts the function and it prints 12.

    function area(width: ____, height: number): number {
      return width * height;
    }
    
    console.log(area(3, 4));
    function area(width: , height: number): number {
  4. Practice · Activity 4 of 7

    Nothing here is annotated. What does tsc report?

    let price = 4.5;
    price = "free";
  5. Practice · Activity 5 of 7

    tsc prints main.ts(5,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. Match each part to what it tells you.

  6. Brain teaser · Activity 6 of 7

    Brain teaser. tsc rejects this file, but you skip tsc and run node main.ts. What does it print?

    const count: number = "3";
    console.log(count + 1);
  7. Apply · Activity 7 of 7

    Mini-task. Write a function formatPrice that takes an amount and a currency and returns text such as 4.50 EUR (amount.toFixed(2) gives the two decimals). Annotate both parameters and the return type, then call it once with console.log. Check it with the editor, or run npx tsc and node main.ts if you have TypeScript installed. Then pass "4.5" instead of 4.5 and read the error.

    Check your work against this list

Build it yourself

Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.

Worked example

A checked shopping total

This program adds up three prices and adds 19% tax. The array and the function parameters are annotated; total is not, because TypeScript infers number from its first value. Run npx tsc: it prints nothing, which means no errors. Then run node main.ts. Try passing "0.19" as the rate and run tsc again. The next lesson installs TypeScript 7.0; the editor on this page checks with TypeScript 6.0, which reports the same errors for every program in this lesson.

main.ts

// A shopping total. tsc checks every use of these values before node runs them.
const prices: number[] = [3.5, 4.25, 2];
let total = 0; // no annotation: TypeScript infers number

function addTax(amount: number, rate: number): number {
  return amount * (1 + rate);
}

for (const price of prices) {
  total += price;
}

console.log("items:", prices.length);
console.log("total:", total);
console.log("with 19% tax:", addTax(total, 0.19).toFixed(2));

Run it with

npx tsc
node main.ts

Output

items: 3
total: 9.75
with 19% tax: 11.60
  • number[] means an array of numbers; pushing text into prices would be a type error.
  • total has no annotation, yet TypeScript treats it as a number everywhere below.
  • The annotations are gone when Node.js runs the file: the output is what the same JavaScript would print.
  • toFixed(2) returns text with two decimals, which is why the last line shows 11.60.
Change it and run it

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the TypeScript compiler (up to 2.1 MB) and keeps it cached. Your code stays on your device.

Exercises

Exercise 1 of 2

Annotate the parameters

This function works in JavaScript, but tsc rejects it: Parameter 'width' implicitly has an 'any' type, and the same for height. Annotate both parameters as numbers, and the return type too. Run the tests: the first one is the type-check.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the TypeScript compiler (up to 2.1 MB) and keeps it cached. Your code stays on your device.

Hints
  1. Hint 1

    An annotation goes after the name, with a colon: width: number.

  2. Hint 2

    The return type goes after the closing bracket of the parameter list: (…): number {

  3. Hint 3

    The finished first line is export function area(width: number, height: number): number {

Show a solution

One way to solve it. Yours can look different and still pass the checks.

export function area(width: number, height: number): number {
  return width * height;
}
Run it on your computer

Install TypeScript 7.0 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.ts

export function area(width, height) {
  return width * height;
}

main.test.ts

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {area} from './main.ts';

test('area(3, 4) is 12', () => {
  assert.equal(area(3, 4), 12, 'area(3, 4) should be 12');
});

test('area(2.5, 2) is 5', () => {
  assert.equal(area(2.5, 2), 5, 'area(2.5, 2) should be 5');
});

package.json

{
  "type": "module"
}

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "nodenext",
    "lib": [
      "esnext",
      "dom"
    ],
    "types": [],
    "strict": true,
    "noEmit": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true,
    "allowImportingTsExtensions": true
  },
  "include": [
    "**/*.ts"
  ],
  "exclude": [
    "**/*.test.ts"
  ]
}

npx tsc needs TypeScript in the folder: run npm install --save-dev typescript there once. tsc checks the types; Node.js runs main.ts by removing them.

Run the program:

npx tsc
node main.ts

Run the checks (needs learnrun.js in the same folder):

npx tsc
node --test
Download learnrun.js

Exercise 2 of 2

Fix what tsc reports

This program should print Points: 42, but it prints Points: 402, and tsc reports two errors. Run the tests or npx tsc, go to each line and column the diagnostics name, and fix the values there. Leave the function as it is: it is correct.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the TypeScript compiler (up to 2.1 MB) and keeps it cached. Your code stays on your device.

Hints
  1. Hint 1

    The first diagnostic is main.ts(6,7): error TS2322. Line 6 stores text in a variable annotated as number.

  2. Hint 2

    The second, on line 7, is about saved: TypeScript inferred string from "40".

  3. Hint 3

    Remove the quotes: numbers are written without them, as 40 and 2.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

export function addPoints(a: number, b: number): number {
  return a + b;
}

const saved = 40;
const bonus: number = 2;
console.log("Points:", addPoints(saved, bonus));
Run it on your computer

Install TypeScript 7.0 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.ts

export function addPoints(a: number, b: number): number {
  return a + b;
}

const saved = "40";
const bonus: number = "2";
console.log("Points:", addPoints(saved, bonus));

main.test.ts

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {runMain} from './learnrun.js';
import {addPoints} from './main.ts';

test('addPoints(40, 2) is 42', () => {
  assert.equal(addPoints(40, 2), 42, 'addPoints(40, 2) should be 42');
});

test('the program prints Points: 42', async () => {
  const out = (await runMain()).trimEnd();
  assert.equal(out, 'Points: 42', `the program printed ${JSON.stringify(out)}`);
});

package.json

{
  "type": "module"
}

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "nodenext",
    "lib": [
      "esnext",
      "dom"
    ],
    "types": [],
    "strict": true,
    "noEmit": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true,
    "allowImportingTsExtensions": true
  },
  "include": [
    "**/*.ts"
  ],
  "exclude": [
    "**/*.test.ts"
  ]
}

npx tsc needs TypeScript in the folder: run npm install --save-dev typescript there once. tsc checks the types; Node.js runs main.ts by removing them.

Run the program:

npx tsc
node main.ts

Run the checks (needs learnrun.js in the same folder):

npx tsc
node --test
Download learnrun.js

Common mistakes

Text in a variable annotated as a number

let count: number = 0;
count = "1";
console.log(count);

What tsc or Node.js prints

main.ts(2,1): error TS2322: Type 'string' is not assignable to type 'number'.

Why, and the fix

count is a number, so it can only ever hold numbers. "1" in quotes is text. Line 2, column 1 is where the text is assigned. Write count = 1 without quotes. If you really need to turn text into a number, convert it with Number("1").

A parameter without a type

function greet(name) {
  return "Hello, " + name;
}

console.log(greet("Ada"));

What tsc or Node.js prints

main.ts(1,16): error TS7006: Parameter 'name' implicitly has an 'any' type.

Why, and the fix

With strict on, TypeScript does not guess what a parameter holds, and it will not silently turn it into any, which would switch checking off for it. Say what you expect: function greet(name: string). The call greet("Ada") then gets checked too.

A typo in a method name

let city = "Berlin";
console.log(city.toUppercase());

What tsc or Node.js prints

main.ts(2,18): error TS2551: Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?

Why, and the fix

Names are case-sensitive, and TypeScript knows every method a string has. It reports the typo before the program runs and suggests the right name. Without tsc, node main.ts stops only when it reaches the line, with TypeError: city.toUppercase is not a function. Write toUpperCase.

TypeScript in the browser: the TypeScript 6.0.3 compiler, Apache-2.0, then your browser’s own engine. Licence and source

Exit ticket

5 questions, no hints. Score 80% or more to complete the lesson.

Finish every activity above to unlock the exit ticket.

Report a problem

Spotted something wrong or unclear? Say what, and it will be checked and fixed.

#

At least 20 characters.

Only if you want a reply.

Key ideas

Types are checked before the program runs

JavaScript learns what a value is only while the program runs. double("21") quietly gives 42, and a mistake in a line that never runs is never reported. TypeScript is JavaScript plus types. Its type-checker, tsc, reads the whole program without running it and reports every place where a value does not fit what the code says, such as text passed where a number is expected. That is static checking: before the program runs, not while it runs. It is not a proof that the program is right. a + b / 2 type-checks and still gives the wrong average: tsc checks that the types fit together, not that the logic does what you meant.

Annotations, inference, and types that disappear

An annotation is a colon and a type after a name: let count: number = 0, or function area(width: number, height: number): number. Parameters usually need one, because TypeScript cannot guess what callers will pass: with strict on, a plain parameter without one is an error. Variables usually do not: in let total = 0, TypeScript infers number, and a later total = "ten" is still an error. When Node.js runs main.ts, it replaces the types with whitespace and runs the rest without checking anything. Types never change what the program does, and node main.ts runs code with type errors too. That is why you run npx tsc first.

The project folder, and reading a diagnostic

The course folder holds main.ts, a package.json with {"type": "module"}, and this tsconfig.json: {"compilerOptions": {"target": "esnext", "module": "nodenext", "lib": ["esnext", "dom"], "types": [], "strict": true, "noEmit": true, "erasableSyntaxOnly": true, "verbatimModuleSyntax": true, "allowImportingTsExtensions": true}, "include": ["**/*.ts"], "exclude": ["**/*.test.ts"]}. Run npx tsc, which finds tsconfig.json by itself and only checks (noEmit), then node main.ts, as two commands. No output from tsc means no errors. Otherwise each line looks like main.ts(5,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. That is the file, line 5, column 20, the error code, and the message: which type was found, which was expected.

Sources

Last reviewed September 30, 2026