TypeScriptWhat Is TypeScript?

What Is TypeScript?

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. It was developed by Microsoft and is maintained as open-source software. In one sentence:

TypeScript is JavaScript with optional type annotations and a compiler that checks them.

This page explains what that means in depth — how the type system works, what happens during compilation, what .d.ts files are, and how TypeScript enhances your development environment.

TypeScript Is JavaScript — Plus Types

Every JavaScript file is already a valid TypeScript file. TypeScript adds a single layer on top of JavaScript: type annotations. These annotations describe what kind of value a variable, parameter, or function return can hold.

TS
// Plain JavaScript — works in TypeScript with zero changes
function add(a, b) {
  return a + b;
}

// TypeScript — same function, with type annotations added
function add(a: number, b: number): number {
  return a + b;
}

// The type annotations are the only difference.
// Both compile to exactly the same JavaScript.
Note
Type annotations are purely a compile-time construct. They are completely stripped out when TypeScript compiles to JavaScript. At runtime, there is no trace of them.
How .ts Files Become .js Files

The TypeScript compiler (tsc) performs two distinct tasks in sequence:

  1. Type checking — reads all your .ts files, analyzes every type annotation and inferred type, and reports any inconsistencies as errors.
  2. Transpilation — strips all type annotations and emits plain .js files that can run in any JavaScript engine.

These two steps are independent. By default, tsc does both. Some setups (like Babel, esbuild, or SWC) perform only transpilation (step 2) — they strip types without type-checking. That is faster but means you lose the error-reporting benefit unless you also run tsc --noEmit separately.

TS
// src/greet.ts — TypeScript source

interface Person {
  name: string;
  age: number;
}

function greet(person: Person): string {
  return `Hello, ${person.name}! You are ${person.age} years old.`;
}

const alice: Person = { name: 'Alice', age: 30 };
console.log(greet(alice));

JS
// dist/greet.js — emitted by tsc (targeting ES2020)

function greet(person) {
  return `Hello, ${person.name}! You are ${person.age} years old.`;
}

const alice = { name: 'Alice', age: 30 };
console.log(greet(alice));

Notice what was removed: the interface Person declaration is gone entirely (interfaces have no runtime representation), and the type annotations : Person, : string, : number are all stripped. The runtime behaviour is identical.

The TypeScript Type System

TypeScript uses a structural type system (also called "duck typing"). Two types are compatible if they have the same shape — the names do not matter.

This is in contrast to nominal type systems (like Java or C#), where two types are only compatible if they are explicitly declared to be related via extends or implements.

TS
// Structural typing — compatible because the shapes match

interface Cat {
  name: string;
  meow(): void;
}

interface Animal {
  name: string;
}

function printName(a: Animal) {
  console.log(a.name);
}

const kitty: Cat = { name: 'Whiskers', meow() {} };

// This works — Cat has at least all the properties of Animal
printName(kitty); // ✓

// In a nominal system (Java/C#), Cat would need to explicitly
// implement Animal for this to compile. Not in TypeScript.
Tip
Structural typing is very natural for JavaScript developers. If an object has the right properties, it fits the type — regardless of how it was created or what it is called.
Structural Typing in Practice

Here is a more concrete example that shows why structural typing matters in daily TypeScript work:

TS
interface Point2D {
  x: number;
  y: number;
}

interface Point3D {
  x: number;
  y: number;
  z: number;
}

function distance(a: Point2D, b: Point2D): number {
  const dx = a.x - b.x;
  const dy = a.y - b.y;
  return Math.sqrt(dx * dx + dy * dy);
}

const p2: Point2D = { x: 0, y: 0 };
const p3: Point3D = { x: 3, y: 4, z: 10 };

// Point3D has x and y (and more), so it satisfies Point2D
distance(p2, p3); // ✓ — TypeScript is fine with this

// This would fail in a nominal system because Point3D does not
// explicitly extend Point2D. TypeScript cares about the shape.
Type Inference

You do not need to annotate every variable. TypeScript infers types from context. When the type can be determined from the initial value or the surrounding code, TypeScript figures it out automatically:

TS
// TypeScript infers all of these types without any annotations:

const name = 'Alice';          // inferred: string
const age = 30;                // inferred: number
const active = true;           // inferred: boolean
const scores = [90, 85, 92];  // inferred: number[]

// Inferred from function return:
function double(n: number) {
  return n * 2;                // return type inferred: number
}

// Inferred from array methods:
const doubled = scores.map(n => n * 2);  // inferred: number[]

// Hover over any variable in VS Code to see its inferred type.
Note
Type inference means TypeScript is often less verbose than people expect. You only need to annotate variables without initial values, function parameters, and places where inference would be wrong or unclear.
Type Annotations in Detail

When you do need to annotate, TypeScript uses the colon syntax:

TS
// Variable annotations
let count: number = 0;
let message: string;
let flags: boolean[];

// Function parameter and return type annotations
function formatPrice(amount: number, currency: string): string {
  return `${currency}${amount.toFixed(2)}`;
}

// Object type annotation (inline)
function printUser(user: { name: string; email: string }): void {
  console.log(`${user.name} <${user.email}>`);
}

// Arrow function annotations
const multiply = (a: number, b: number): number => a * b;

// Optional parameters (the ? makes the parameter optional)
function greet(name: string, title?: string): string {
  return title ? `Hello, ${title} ${name}` : `Hello, ${name}`;
}
What TypeScript Looks Like vs JavaScript

Here is the same non-trivial program written in JavaScript and TypeScript, side by side:

JS
// JavaScript version — no types

function processOrder(order) {
  if (!order || !order.items) {
    throw new Error('Invalid order');
  }

  const total = order.items.reduce((sum, item) => {
    return sum + item.price * item.quantity;
  }, 0);

  return {
    id: order.id,
    total,
    itemCount: order.items.length,
  };
}

TS
// TypeScript version — with types

interface OrderItem {
  name: string;
  price: number;
  quantity: number;
}

interface Order {
  id: string;
  items: OrderItem[];
}

interface OrderSummary {
  id: string;
  total: number;
  itemCount: number;
}

function processOrder(order: Order): OrderSummary {
  const total = order.items.reduce((sum, item) => {
    return sum + item.price * item.quantity;
  }, 0);

  return {
    id: order.id,
    total,
    itemCount: order.items.length,
  };
}

The TypeScript version is longer, but:

  • You know exactly what an Order looks like — it is self-documenting
  • If you pass the wrong shape, the compiler tells you immediately
  • Your editor autocompletes order.items, item.price, item.quantity
  • If you rename price to unitPrice, the compiler finds every broken reference
Declaration Files (.d.ts)

A .d.ts file (pronounced "dot dee dot tee ess" or "declaration file") contains only type information — no executable code. It tells TypeScript what the types of a JavaScript library are, without the library needing to be rewritten in TypeScript.

When you install @types/react, you get a .d.ts file that describes every export of the React library. TypeScript reads it to give you type checking and autocomplete for React, even though React itself is written in JavaScript.

TS
// Example: a simplified .d.ts file for a JavaScript utility library

// math-utils.d.ts
export declare function add(a: number, b: number): number;
export declare function subtract(a: number, b: number): number;
export declare function clamp(value: number, min: number, max: number): number;

export interface Vector2 {
  x: number;
  y: number;
}

export declare function dot(a: Vector2, b: Vector2): number;

You rarely write .d.ts files by hand. TypeScript can generate them automatically from your own .ts files when you build a library (using the declaration: true tsconfig option). For third-party JS libraries, the community maintains the @types packages on npm.

TypeScript as a Development Tool

It is worth emphasizing: TypeScript is a development-time tool. It helps you while you are writing and building code. At runtime, the JavaScript engine runs plain .js — it knows nothing about TypeScript.

This has important implications:

  • TypeScript types cannot validate data from external sources (API calls, user input, localStorage) at runtime — use Zod, Yup, or io-ts for that

  • A type assertion (the as keyword) does not cast or convert a value — it just tells the compiler to treat it as a different type

  • TypeScript cannot prevent runtime errors caused by incorrect data from outside your system

  • The TypeScript compiler has zero impact on your production bundle size or execution speed

  • You can have 100% type-correct TypeScript that still throws runtime errors — TypeScript only checks what it can see

Warning
Never use TypeScript type assertions (as SomeType) as a substitute for actual runtime validation. A type assertion is a promise to the compiler — it does not perform any check at runtime.
How TypeScript Enhances IDEs

TypeScript's type information powers a dramatically better editor experience. The TypeScript language server (a background process that editors communicate with) provides:

Autocomplete When you type user., the editor shows every property and method of user's type. No guessing, no documentation lookup required.

Go to Definition Click on any function, variable, or type name and jump directly to where it is defined — even inside node_modules. This works because TypeScript knows the exact shape of every value.

Find All References Ask the editor to show every place a function or variable is used. TypeScript traverses the entire codebase, following imports across files.

Safe Rename Rename a function or property and TypeScript updates every reference in every file — including files you did not know used it.

Inline Error Highlighting Errors appear as red squiggles as you type, before you save. You see the problem at the exact character that caused it, with a plain-English explanation.

Hover Documentation Hovering over any symbol shows its type, its JSDoc comment, and a link to the source.

TS
interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
}

const user: User = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  createdAt: new Date(),
};

// In VS Code, after typing "user." you get autocomplete:
// id, name, email, createdAt — and their types
user.name;        // string
user.createdAt;   // Date

// Typo — caught immediately, not at runtime:
user.emial;       // Error: Property 'emial' does not exist on type 'User'. Did you mean 'email'?
TypeScript vs JavaScript Feature Comparison

Feature

JavaScript

TypeScript

Type annotations

Not available

name: string, age: number

Interfaces

Not available

interface User { name: string }

Enums

Not available

enum Direction { Up, Down, Left, Right }

Generics

Not available

function identity<T>(x: T): T

Type inference

None

Full inference from initial values

Access modifiers

Not available

public, private, protected, readonly

Optional chaining

Yes (ES2020)

Yes + type-aware

Nullish coalescing

Yes (ES2020)

Yes + type-aware

Decorators

Stage 3 proposal

Supported (with experimentalDecorators)

Quick Reference
  • TypeScript = JavaScript + static types, compiled to plain JavaScript

  • Uses structural typing — compatibility is based on shape, not name

  • Type inference: TypeScript deduces types without annotations when possible

  • .d.ts files contain type information for JavaScript libraries

  • Types are completely erased at compile time — zero runtime overhead

  • TypeScript is a development tool; it cannot validate runtime data

  • The language server powers autocomplete, go-to-def, find-refs, safe rename

Success
You now understand the mechanics of TypeScript — how the type system works, how compilation happens, what declaration files are, and why TypeScript makes editors dramatically more powerful. The next page makes the case for why you should use it.