JavaScript with types
TypeScript is a superset of JavaScript: every valid JS is valid TS, but TS adds a static type system. You annotate what shape your data has and a compiler verifies it before running the program.
function add(a: number, b: number): number {
return a + b;
}
add(2, 3); // OK
add(2, "3"); // ❌ COMPILE error: "3" is not a number
In JavaScript that add(2, "3") would run and return "23" (a silent
bug). In TypeScript it doesn't even compile.
What you gain
- Catch errors at compile time, not in production: typos, swapped
arguments, properties that don't exist, unhandled
undefined. - Reliable autocomplete and refactors: the editor knows the exact shape of each object, so it suggests fields and warns you if you rename something.
- Living documentation: the signature
function createUser(dto: UserDTO)already says what the function expects.
tsconfig and compilation
The project configuration lives in tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"strict": true,
"outDir": "dist"
}
}
strict: true enables the most useful checks (among them
strictNullChecks, which forces you to handle null/undefined).
To run your code you have two paths:
- Compile with
tscto JavaScript and run the output with Node:tsc && node dist/index.js. ts-node(ortsx) to run the.tsdirectly in development:ts-node src/index.ts.
Key: at runtime it's still JavaScript
The compiler erases the types (type erasure). The .js that Node runs
has no trace of : number nor of interface:
interface User { name: string; age: number; }
function greet(u: User) {
return "Hello " + u.name;
}
compiles roughly to:
function greet(u) {
return "Hello " + u.name;
}
The interface disappears. This has a huge consequence, which we'll see at the
end: types don't exist at runtime, so they don't protect
against external data (what arrives over HTTP, from the database, from a
JSON.parse...). Types guarantee that your code is consistent; they don't
guarantee that the input data has the promised shape.