Skip to content
TR

Phase 1 · Foundations · Lesson 1.1

Beginner

Why TypeScript: the compiler and tsconfig

What TypeScript adds to JavaScript, what the compiler actually does, and the handful of tsconfig options that matter on day one.

15 min

JavaScript tells you about a typo when a user hits it. TypeScript tells you while you type. That is the whole pitch: catch mistakes before the code runs, and get an editor that knows what every value is.

TypeScript is JavaScript plus types

Every JavaScript program is (almost) a valid TypeScript program. TypeScript adds one thing: type annotations, which describe what a value is allowed to be.

function greet(name: string): string {
  return `Hello, ${name.toUpperCase()}`;
}
 
greet("Ada");
// @ts-expect-error -- Argument of type 'number' is not assignable to parameter of type 'string'.
greet(42);

▶ Try it in the TypeScript Playground

name: string says "this parameter must be a string". The call greet(42) is rejected before the code ever runs. In plain JavaScript it would crash at runtime with name.toUpperCase is not a function.

Types disappear at runtime

The TypeScript compiler, tsc, does two separate jobs:

  1. Type-check: read your code and report errors.
  2. Emit: strip the types and write plain JavaScript.

The emitted JavaScript for the function above is simply:

function greet(name) {
  return `Hello, ${name.toUpperCase()}`;
}

So types are a compile-time only safety net. There is no type information left when the code runs. You can't write if (typeof x === "User"); to check a shape at runtime you check the value itself (you'll learn that in the narrowing lesson).

Quick check

What does the browser receive for let count: number = 1; after compiling?

Type inference: you don't annotate everything

TypeScript infers types from values. Most variables need no annotation at all:

let city = "Dhaka";
//  ^? let city: string
 
const port = 8080;
//    ^? const port: 8080   (a const can never change, so its type is the exact value)
 
const scores = [90, 85, 77];
//    ^? const scores: number[]
 
// @ts-expect-error -- Type 'number' is not assignable to type 'string'.
city = 42;

A good rule: annotate function parameters and public APIs; let inference handle local variables.

Quick check

What type does TypeScript infer for x?

const x = "hello";

tsconfig.json: the options that matter first

A project's compiler settings live in tsconfig.json. tsc --init generates one with dozens of options; these are the ones to understand first:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "strict": true,
    "noEmitOnError": true,
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"]
}
OptionWhat it does
targetWhich JavaScript version to emit (newer = less rewriting).
moduleWhich module system the output uses (ESNext for import/export).
strictTurns on the whole family of strict checks. Always on for new code.
noEmitOnErrorDon't write JavaScript if there are type errors.
outDir / rootDirWhere compiled files go, and where sources live.
includeWhich files belong to the project.

What strict turns on

strict: true is a bundle. The two you'll feel most:

  • strictNullChecks: null and undefined are their own types. A string can't secretly be null.
  • noImplicitAny: if TypeScript can't infer a type, it's an error instead of silently becoming any.
function lengthOf(text: string | null) {
  // @ts-expect-error -- 'text' is possibly 'null'.
  return text.length;
}
 
function safeLengthOf(text: string | null) {
  return text === null ? 0 : text.length; // checked first, so this is fine
}

Without strictNullChecks, the first function compiles and crashes at runtime on null. That single flag prevents a whole class of production bugs.

Spot the error

This compiles in plain JavaScript but not in strict TypeScript. Which lines fail, and why?

function total(prices) {
  return prices.reduce((sum, p) => sum + p, 0);
}
Show the answer

Line 1: under noImplicitAny (part of strict), the parameter prices has no type and nothing to infer it from, so it's an implicit any error: Parameter 'prices' implicitly has an 'any' type. Line 2 fails too, as a knock-on: because prices is any, the callback's sum and p get no type from reduce either, so they're implicit any as well. Fix it by saying what prices is:

function total(prices: number[]): number {
  return prices.reduce((sum, p) => sum + p, 0);
}

Running the compiler

npm install --save-dev typescript
npx tsc --init      # create tsconfig.json
npx tsc             # type-check and emit
npx tsc --noEmit    # type-check only (common in CI when a bundler emits)
npx tsc --watch     # re-check on every save

Many tools (Vite, esbuild, Bun, Node's type stripping) only strip types without checking them. They're fast because they skip job 1. That's why projects still run tsc --noEmit in CI: it's the only step that actually checks your types.

Recap

  • TypeScript = JavaScript + type annotations, checked before runtime.
  • tsc type-checks and emits JavaScript; types are erased, so nothing is checked at runtime.
  • Inference handles most local variables; annotate parameters and public APIs.
  • const infers literal types ("hello"), let widens (string).
  • Always use strict: true; strictNullChecks and noImplicitAny catch the most bugs.
  • Fast bundlers strip types without checking; run tsc --noEmit in CI.

Interview cards

1 / 6