Skip to main content
Advertisement

1.1 What is TypeScript?

When building large-scale applications with JavaScript, certain categories of bugs tend to appear repeatedly. Passing a string where a number is expected, accessing a property that doesn't exist, or using undefined as if it were a number — these mistakes can't be caught until the code actually runs, and the larger a project grows, the harder they become to track down. TypeScript was created specifically to solve this problem.

Defining TypeScript

TypeScript is an open-source programming language released by Microsoft in 2012. Its chief architect is Anders Hejlsberg, the creator of C#. TypeScript's official definition is "a superset of JavaScript."

A superset is a language that includes all the syntax of an existing language while adding extra features on top. In other words, every valid JavaScript program is also a valid TypeScript program. You can rename any .js file to .ts and it will compile without errors. This property allows TypeScript to be adopted incrementally in existing JavaScript projects.

The core feature TypeScript adds on top of JavaScript is a Static Type System.

Dynamic Typing vs Static Typing

JavaScript is a dynamically typed language. The type of a variable is determined at the moment the code runs (runtime).

// JavaScript — types are determined at runtime
let value = 42; // number
value = "hello"; // can change to string — no error
value = true; // can change to boolean — no error

TypeScript is a statically typed language. The type of a variable is determined at the moment the code is written (compile time).

// TypeScript — types are determined at compile time
let value: number = 42;
value = "hello"; // Error: Type 'string' is not assignable to type 'number'
value = true; // Error: Type 'boolean' is not assignable to type 'number'

The error appears in your editor the moment you type the code — before you ever run it. That is the essence of a static type system.

The Origins of TypeScript

Back in 2012, Microsoft was developing large-scale JavaScript applications internally. Working with hundreds of thousands of lines of JavaScript across teams of hundreds of developers surfaced serious problems.

Problem 1: No code navigation When modifying a function, it was difficult to understand how it was used across the rest of the project. Renaming a parameter or changing a type made it impossible to know what effects would ripple through other files.

Problem 2: Dangerous refactoring Renaming a property on an object required manually hunting down every piece of code that referenced it. Miss even one and the bug wouldn't surface until runtime.

Problem 3: No IDE support Without type information, IDEs had no way of knowing "what methods does this variable have?" Autocomplete simply didn't work properly.

Problem 4: Implicit type coercion Operations like 5 + "3" silently return "53" without any error. Unintended type conversions happening quietly in the background caused hard-to-find bugs.

Anders Hejlsberg's team designed a language that adds type information to JavaScript to solve these problems — and that language is TypeScript.

The Compilation Pipeline and Type Erasure

TypeScript files (.ts) cannot be run directly in a browser or Node.js. They must first be converted to JavaScript files (.js) via the TypeScript compiler (tsc).

Write .ts file


tsc (TypeScript Compiler)
├─ Type Checking
│ └─ Prints error messages when issues are found
└─ Transpilation
└─ Removes type annotations
└─ Produces .js file


.js file (pure JavaScript)


Runs in Node.js / browser

There is one important concept here: Type Erasure.

TypeScript's type information is completely removed after compilation. The final .js file contains no type annotations whatsoever.

// TypeScript source code (hello.ts)
function greet(name: string): string {
return `Hello, ${name}!`;
}

const user: string = "Alice";
console.log(greet(user));
// Compiled JavaScript output (hello.js)
function greet(name) {
return `Hello, ${name}!`;
}

const user = "Alice";
console.log(greet(user));

All type annotations like : string and : number are gone. Type information exists only during development; at runtime there is no trace of it.

Real JavaScript Bugs That TypeScript Catches

These are the kinds of errors every JavaScript developer has encountered at some point.

undefined is not a function

// JavaScript — error occurs at runtime
const user = {
name: "Alice",
getAge: function() { return 30; }
};

user.getname(); // TypeError: user.getname is not a function
// You have to run the code to discover the error
// TypeScript — error detected the moment you type it
const user = {
name: "Alice",
getAge: function(): number { return 30; }
};

user.getname();
// Error: Property 'getname' does not exist on type '...'
// Did you mean to call 'getAge'?

Cannot read properties of undefined

// JavaScript — accessing a property missing from an API response
function displayUserCity(user) {
console.log(user.address.city); // runtime error if user.address is undefined
}

displayUserCity({ name: "Bob" }); // TypeError thrown
// TypeScript — error caught before you even access it
interface User {
name: string;
address?: {
city: string;
};
}

function displayUserCity(user: User): void {
console.log(user.address.city);
// Error: Object is possibly 'undefined'
// Correct code: console.log(user.address?.city);
}

Unintended type coercion

// JavaScript — silently returns a wrong result
function calculateTotal(price, quantity) {
return price * quantity;
}

const total = calculateTotal("100", 3); // Returns 300, not "100100100" (string * number = number)
// But what if price is "100abc"? Returns NaN
// A common bug when passing form input values directly
// TypeScript — blocks incorrect input at compile time
function calculateTotal(price: number, quantity: number): number {
return price * quantity;
}

const total = calculateTotal("100", 3);
// Error: Argument of type 'string' is not assignable to parameter of type 'number'

The Practical Benefits of a Static Type System

Compile-time error detection

Bugs that could only be found at runtime are now caught at the moment the code is written. This prevents users from ever encountering those errors after deployment.

Improved IDE support

With type information, the IDE can fully understand your code. As a result:

  • Autocomplete (IntelliSense): Automatically suggests object properties, function parameters, and return types
  • Instant error highlighting: Displays errors with red underlines before you even save the file
  • Safe refactoring: Renaming variables or functions safely updates every reference in one operation
  • Go to Definition: Ctrl+Click jumps directly to where a function or type is defined
  • Find All References: Locates every place a function or type is used

Safe refactoring

When changing a function signature in a large codebase, TypeScript immediately highlights compatibility errors everywhere that function is used. You can see the full blast radius of a change and make it safely.

Code as documentation

Type annotations act as documentation in their own right.

// Without types — what does this function accept and return?
function processOrder(order, options) { ... }

// With types — immediately clear at a glance
function processOrder(order: Order, options: ProcessOptions): Promise<OrderResult> { ... }

JS vs TS Comparison

AspectJavaScriptTypeScript
When types are determinedRuntimeCompile time
When errors are foundAfter executionWhile writing / compiling
IDE autocompleteLimitedFull support
Refactoring safetyLowHigh
MaintainabilityDifficult at scaleWell suited for large projects
Learning curveLowModerate
ExecutionRuns directlyCompiled by tsc first
File extension.js.ts, .tsx
Existing JS codeWorks as-is

Practical Example: JS Bugs That TS Catches Side by Side

Let's compare scenarios that commonly occur in real codebases.

Scenario: E-commerce discount calculator

// JavaScript version — there's a bug hiding here
function applyDiscount(price, discountPercent) {
return price - (price * discountPercent / 100);
}

// Developer accidentally passes a string
const itemPrice = "50000"; // value pulled from a form input
const discount = 10;

const finalPrice = applyDiscount(itemPrice, discount);
console.log(finalPrice); // 45000 — happens to be correct (string * number = number)

// But what about this case?
const finalPrice2 = applyDiscount("$500", 10);
console.log(finalPrice2); // NaN — only discovered at runtime
// TypeScript version — catches the problem at compile time
function applyDiscount(price: number, discountPercent: number): number {
return price - (price * discountPercent / 100);
}

const itemPrice = "50000"; // string from a form field
const discount = 10;

const finalPrice = applyDiscount(itemPrice, discount);
// Error: Argument of type 'string' is not assignable to parameter of type 'number'
// Compilation fails — the bug must be fixed before deployment

// Correct usage
const finalPrice2 = applyDiscount(Number(itemPrice), discount);
console.log(finalPrice2); // 45000

Scenario: Handling an API response

// JavaScript version
async function fetchUserProfile(userId) {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();

// You can access fields immediately without knowing the shape, but...
return `${data.firstName} ${data.lastName}`;
// What if the actual API returns first_name/last_name instead of firstName/lastName?
// "undefined undefined" — only discovered at runtime
}
// TypeScript version
interface UserProfile {
id: number;
firstName: string;
lastName: string;
email: string;
}

async function fetchUserProfile(userId: number): Promise<string> {
const response = await fetch(`/api/users/${userId}`);
const data: UserProfile = await response.json();

// The IDE knows exactly what properties data has and autocompletes them
return `${data.firstName} ${data.lastName}`;
// If you try to access first_name instead?
// Error: Property 'first_name' does not exist on type 'UserProfile'
}

The TypeScript Ecosystem and Adoption

TypeScript has become the de facto standard language in the JavaScript ecosystem.

Major frameworks and libraries

Almost every major JavaScript framework is written in TypeScript or officially supports TypeScript.

Tool / FrameworkTypeScript support level
AngularTypeScript-only (designed with TS from the start)
React.tsx files, official type definitions provided
Vue 3Rewritten in TypeScript, optimized for Composition API
Next.jsTypeScript configuration provided by default
NestJSTypeScript-only Node.js backend framework
DenoTypeScript as a first-class language
BunRuns TypeScript files natively

DefinitelyTyped — Adding types to JavaScript libraries

Even JavaScript libraries not written in TypeScript can be used in a type-safe way. The DefinitelyTyped project provides type definitions (.d.ts files) for thousands of libraries, published as @types/xxx packages.

npm install --save-dev @types/node      # Node.js built-in module types
npm install --save-dev @types/express # Express.js types
npm install --save-dev @types/lodash # lodash types
npm install --save-dev @types/jest # Jest testing framework types

Newer libraries ship their own type definitions. Libraries like axios, zod, and prisma support TypeScript out of the box without needing a separate @types package.

Type declaration files (.d.ts)

A .d.ts file contains only type definitions — no implementation code. TypeScript uses them to understand the API of JavaScript libraries.

// Excerpt from node_modules/@types/node/index.d.ts (illustrative)
declare module 'path' {
function join(...paths: string[]): string;
function resolve(...paths: string[]): string;
const sep: string;
}

Because of this file, after import path from 'path', calling path.join gives you full autocomplete and type checking.

TypeScript Version History

TypeScript has steadily evolved since its initial release in 2012. Key milestones:

VersionYearKey features
1.02014Official GA release
2.02016strictNullChecks, Tagged Unions
3.02018Project References, Tuples improvements
4.02020Variadic Tuples, unknown in catch clauses
4.52021Awaited<T>, import type modifiers
5.02023Decorator standardization, const type parameters
5.42024NoInfer<T>, closure type narrowing improvements
5.52024Inferred type predicates, regex syntax checking

The TypeScript 5.x series focuses on performance improvements and more precise type inference rather than sweeping new features.

Pro Tips

TypeScript has no effect at runtime

TypeScript's type system is purely a development tool. Since all type information is stripped out during compilation, the final JavaScript has no trace of types — there is zero impact on runtime performance and bundle size does not increase.

Type annotations are not "code that executes" — they are a contract between the developer and the compiler.

Types are just a development aid

Even when TypeScript finds type errors, compilation generally continues and a JavaScript file is still produced. (Setting noEmitOnError: true prevents output when errors exist.) Code with type errors can still run.

This is extremely useful when incrementally migrating a JavaScript project to TypeScript.

You can add types to any JS library

Existing JavaScript libraries can still be used from TypeScript. The DefinitelyTyped project (@types/xxx packages) provides type definitions for thousands of JavaScript libraries.

npm install --save-dev @types/node    # Node.js type definitions
npm install --save-dev @types/lodash # lodash type definitions

TypeScript is a "type eraser" language

TypeScript = JavaScript + type annotations → compile → JavaScript (types removed)

Once you understand this, there's nothing to fear about TypeScript. Learning TypeScript means learning how to annotate the JavaScript you already know with types. If you already know JavaScript, you already know 80% of TypeScript syntax.

Summary

ConceptDescription
TypeScriptA superset of JavaScript with a static type system
SupersetAll JS code is valid TS code
Static typingTypes are determined and checked at compile time
tscThe TypeScript compiler — transforms .ts to .js
Type erasureType information is removed after compilation
Runtime impactNone — types are purely a development tool
OriginMicrosoft, 2012, Anders Hejlsberg
PurposeImprove stability and maintainability of large-scale JS projects

The next chapter covers how to install TypeScript and set up a development environment to start using it.

Advertisement