Phase 1 · Foundations · Lesson 1.1
BeginnerWhy 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:
- Type-check: read your code and report errors.
- 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"]
}| Option | What it does |
|---|---|
target | Which JavaScript version to emit (newer = less rewriting). |
module | Which module system the output uses (ESNext for import/export). |
strict | Turns on the whole family of strict checks. Always on for new code. |
noEmitOnError | Don't write JavaScript if there are type errors. |
outDir / rootDir | Where compiled files go, and where sources live. |
include | Which files belong to the project. |
What strict turns on
strict: true is a bundle. The two you'll feel most:
strictNullChecks:nullandundefinedare their own types. Astringcan't secretly benull.noImplicitAny: if TypeScript can't infer a type, it's an error instead of silently becomingany.
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 saveMany 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.
tsctype-checks and emits JavaScript; types are erased, so nothing is checked at runtime.- Inference handles most local variables; annotate parameters and public APIs.
constinfers literal types ("hello"),letwidens (string).- Always use
strict: true;strictNullChecksandnoImplicitAnycatch the most bugs. - Fast bundlers strip types without checking; run
tsc --noEmitin CI.
Interview cards
1 / 6