Skip to main content
TypeScript · lesson 1 of 21
TypeScript beginner Lesson 1 of 21

Introduction to TypeScript

Understand what TypeScript is, why it exists, how it differs from JavaScript, and where it fits in the modern web ecosystem.

By SkillByExample · Updated

What Is TypeScript?

TypeScript is a statically typed superset of JavaScript developed and maintained by Microsoft. You annotate variables, function parameters and return values with types, and the TypeScript compiler (tsc) checks those annotations before your code runs. Every example here was run with TypeScript 7.0.2 on Node v24.18.1, in a folder with no tsconfig.json.

Start with plain JavaScript. Save this as plain.js and run it:

function add(a, b) {
  return a + b;
}

console.log(add(1, 2));
console.log(add(1, "hello"));
$ node plain.js
3
1hello

The second call is a bug, but JavaScript doesn’t complain. + sees a string, so it concatenates instead of adding, and the result is a wrong answer rather than a crash.

Now the same function with types, saved as add.ts:

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

console.log(add(1, 2));
console.log(add(1, "hello"));
$ npx tsc add.ts
add.ts(6,20): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

tsc points at line 6, column 20, which is the "hello" argument, before anything runs. That is the core value of TypeScript: the mistake is reported where you wrote it, not when a user hits that code path.

Why TypeScript Exists

JavaScript was designed for small scripts in web pages. As applications grew into hundreds of thousands of lines across large teams, the lack of types created real, costly problems:

  • Refactoring is risky. Renaming a function or changing its signature in a large JS codebase means manually hunting every call site. TypeScript’s compiler does that for you automatically and flags every place that breaks.
  • Tooling is limited. Without types, editors cannot reliably autocomplete, detect unused variables, or show inline documentation. Every object is opaque until you read the source.
  • Bugs surface late. A typo in a property name or passing the wrong argument type shows up only when a user hits that code path at runtime — which may be rarely, or in a critical flow.

TypeScript addresses all three. It was first released in 2012 and has become the dominant choice for large-scale JavaScript projects.

TypeScript vs JavaScript

Understanding the key differences helps you know when TypeScript is pulling its weight and when you might prefer plain JS for a small script.

FeatureJavaScriptTypeScript
Type systemDynamic, checked at runtimeStatic, checked by tsc before running
SyntaxES standardES standard + type annotations
Runs in browsersDirectlyAfter compiling or bundling to JS
Runs in Node.jsDirectlyDirectly since Node 22.18 (types stripped, not checked), or after compiling
Tooling (autocomplete, refactoring)LimitedExcellent
Catch errors earlyNoYes, if you run tsc

The “not checked” in that table matters. Node 24 can run add.ts itself, with no compile step:

$ node add.ts
3
1hello

Node removed the annotations and ran what was left, which is exactly plain.js. It never looked at the types, so the bug printed 1hello again. Running TypeScript and type-checking it are two separate steps, and only tsc (or your editor, which uses the same checker) does the second one.

A Realistic Example

This example shows how TypeScript prevents a whole class of bug that is easy to introduce and hard to track down in plain JavaScript. Save it as welcome.ts:

interface User {
  id: number;
  name: string;
  email: string;
}

function sendWelcomeEmail(user: User): void {
  console.log(`Sending email to ${user.email}`);
}

const newUser = { id: 1, name: "Alice", email: "alice@example.com" };
sendWelcomeEmail(newUser);

// An incomplete object: Bob has no email
sendWelcomeEmail({ id: 2, name: "Bob" });
$ npx tsc welcome.ts
welcome.ts(15,18): error TS2741: Property 'email' is missing in type '{ id: number; name: string; }' but required in type 'User'.

Alice’s call compiles even though newUser never mentions User. TypeScript compares shapes, not names, and her object has every property User needs. Bob’s object is missing email, and the error names that exact property. Without TypeScript, this bug would only show up at runtime as an email sent to undefined (Practice 2 runs it and shows that).

The TypeScript Ecosystem

TypeScript is deeply integrated into the modern JavaScript ecosystem, which means you benefit from it immediately in any standard project:

  • Frameworks: React, Angular, Vue, Svelte, Next.js, Nuxt, and Remix all have first-class TypeScript support. Many are written in TypeScript themselves.
  • Runtimes: Node.js 22.18+ runs .ts files by stripping their types, as shown above, and tsx does the same for syntax Node can’t strip. Deno and Bun run TypeScript without any setup. None of them type-check; tsc still does that.
  • Compiler: TypeScript 7 (typescript@latest since July 2026) is a native rewrite of the compiler in Go. It checks the same language much faster than the JavaScript-based TypeScript 6, and turns options that 6.0 deprecated, such as --target ES5, into errors.
  • Package ecosystem: Most popular npm packages ship with .d.ts declaration files or have community-maintained @types/* packages on DefinitelyTyped, so you get types for libraries you didn’t write.
  • Tooling: VS Code, WebStorm, and other editors provide full IntelliSense, inline errors, and refactoring tools powered by the TypeScript language server.

Compilation Pipeline

tsc does two jobs: it type-checks your .ts files, then it writes plain .js files with the types removed. Save this as pipeline.ts, compile it, and look at what it produced:

interface Point {
  x: number;
  y: number;
}

const start: Point = { x: 0, y: 0 };
const distance = (p: Point): number => Math.hypot(p.x, p.y);

console.log(distance({ x: 3, y: 4 }), distance(start));
$ npx tsc pipeline.ts
$ cat pipeline.js
"use strict";
const start = { x: 0, y: 0 };
const distance = (p) => Math.hypot(p.x, p.y);
console.log(distance({ x: 3, y: 4 }), distance(start));
$ node pipeline.js
5 0

The interface is gone completely, because it only ever existed for the checker. The : Point and : number annotations are gone too. What’s left is the same JavaScript you would have written by hand, plus a "use strict" line, because TypeScript 6 and later compile in strict mode by default.

The compiled JavaScript can also target older environments. The target option decides which newer syntax tsc rewrites into older syntax, and module decides how import/export are written out. You control both through tsconfig.json, covered in the next tutorial.

Key Takeaways

  • TypeScript adds a static type system to JavaScript without changing the runtime behavior.
  • Errors caught by the compiler never reach your users, but only if something runs the compiler. node file.ts runs code without checking it.
  • tsc still writes JavaScript when there are type errors, unless you set noEmitOnError.
  • The tooling improvements — autocomplete, safe refactoring, inline docs — are often as valuable as the type safety itself.
  • TypeScript compiles to plain JavaScript, so it runs everywhere JavaScript runs.

Practice

You need Node.js and the TypeScript compiler (npm install -D typescript). The solutions were run with TypeScript 7.0.2 on Node v24.18.1, with no tsconfig.json in the folder.

  1. Many older tutorials show the JavaScript add and the typed add together in one snippet. Put both functions in one file, both.ts, with the two calls after them, and compile it. Is the error the TS2345 you saw above?
Solution
// JavaScript — no type information, no safety net
function add(a, b) {
  return a + b;
}

// TypeScript — types are explicit, mistakes are caught immediately
function add(a: number, b: number): number {
  return a + b;
}

add(1, 2);       // fine
add(1, "hello"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'
$ npx tsc both.ts
both.ts(2,10): error TS2393: Duplicate function implementation.
both.ts(2,14): error TS7006: Parameter 'a' implicitly has an 'any' type.
both.ts(2,17): error TS7006: Parameter 'b' implicitly has an 'any' type.
both.ts(7,10): error TS2393: Duplicate function implementation.

No. One file can’t hold two bodies for the same function name, so tsc reports TS2393 on both declarations. Strict mode (on by default) also rejects the untyped a and b with TS7006. The calls are checked against the first add, whose parameters are implicitly any, so "hello" is accepted and the TS2345 the comment promises never appears. (Swap the two functions and it does, on line 12, alongside the same TS2393s.) Comments can lie. Run the code.

  1. Compile welcome.ts from the realistic example, then run the welcome.js that tsc produced anyway. What does Bob’s line print?
Solution
$ npx tsc welcome.ts
welcome.ts(15,18): error TS2741: Property 'email' is missing in type '{ id: number; name: string; }' but required in type 'User'.
$ node welcome.js
Sending email to alice@example.com
Sending email to undefined

tsc reported the error and still wrote welcome.js, because type errors don’t block output by default. Running it shows the bug the lesson describes: no crash, just undefined going out as an email address.

  1. Make a different mistake in a copy called typo.ts: delete Bob’s call so only Alice’s call remains, written inline, then misspell the property inside the function as user.emial. Compile it and run the result.
Solution
interface User {
  id: number;
  name: string;
  email: string;
}

function sendWelcomeEmail(user: User): void {
  console.log(`Sending email to ${user.emial}`);
}

sendWelcomeEmail({ id: 1, name: "Alice", email: "alice@example.com" });
$ npx tsc typo.ts
typo.ts(8,40): error TS2551: Property 'emial' does not exist on type 'User'. Did you mean 'email'?
$ node typo.js
Sending email to undefined

Because the compiler knows every property of User, it can suggest the right name. At runtime the typo becomes a silent undefined, even though the caller passed a correct object.

  1. Watch the target option rewrite syntax. Save this as target.ts. ?? (nullish coalescing) arrived in ES2020. Compile it with --target ES5, then --target ES2019, then --target ES2020, and look at target.js after each run.

    const greet = (name?: string): string => `Hello, ${name ?? "stranger"}`;
    console.log(greet("Ada"), greet());
Solution
$ npx tsc --target ES5 target.ts
error TS5108: Option 'target=ES5' has been removed. Please remove it from your configuration.
$ npx tsc --target ES2019 target.ts
$ cat target.js
"use strict";
const greet = (name) => `Hello, ${name !== null && name !== void 0 ? name : "stranger"}`;
console.log(greet("Ada"), greet());
$ npx tsc --target ES2020 target.ts
$ cat target.js
"use strict";
const greet = (name) => `Hello, ${name ?? "stranger"}`;
console.log(greet("Ada"), greet());
$ node target.js
Hello, Ada Hello, stranger

TypeScript 6.0 deprecated ES5 output and TypeScript 7 removed it, so the oldest target left is ES2015. For ES2019, tsc rewrites ?? into the equivalent !== null && !== void 0 check. For ES2020 it keeps ?? as written, because that version of JavaScript already has it. Both builds drop the ?: string and : string annotations, and both print the same thing.

Frequently Asked Questions

Is TypeScript a different programming language?
TypeScript is a superset of JavaScript. Every valid JavaScript file is also valid TypeScript. TypeScript adds a static type system, and the compiler removes those types to produce plain JavaScript.
Can Node.js run TypeScript files directly?
Yes. Node.js 22.18 and later, including Node 24, run a .ts file with plain node file.ts by stripping the type annotations first. Node does not type-check anything, so a file full of type errors still runs. Browsers still need the code compiled or bundled to JavaScript first.
Does TypeScript check types at runtime?
No. Types exist only while the compiler checks your code. They are erased from the JavaScript that actually runs, so a value of the wrong type that arrives at runtime, for example from JSON or an API, is not caught unless you validate it yourself.
Does tsc still produce JavaScript when there are type errors?
Yes, by default. tsc reports the errors and exits with a non-zero code, but it still writes the .js file. Set noEmitOnError to true in tsconfig.json if you want a type error to block the output.
Should I learn JavaScript before TypeScript?
Yes. TypeScript builds on JavaScript's syntax and runtime model. A solid understanding of JavaScript makes TypeScript much easier to learn, because everything the program does at runtime is still JavaScript.