TypeScriptTypeScript vs JavaScript

TypeScript vs JavaScript

TypeScript and JavaScript are deeply connected — TypeScript is a strict superset of JavaScript, meaning every valid JavaScript program is also valid TypeScript. But the differences between them shape how you write, maintain, and reason about code. This page gives you an honest, side-by-side comparison across the dimensions that matter most.

The Relationship Explained

The "superset" relationship is important:

  • All JavaScript is valid TypeScript — you can rename a .js file to .ts and it usually compiles.
  • TypeScript adds an extra layer (types, interfaces, enums, generics) that gets erased during compilation.
  • The final output is plain JavaScript — browsers and Node.js never see TypeScript syntax.

TS
// TypeScript source
function add(a: number, b: number): number {
  return a + b;
}

const result: number = add(2, 3);

JS
// Compiled JavaScript output (types erased)
function add(a, b) {
  return a + b;
}

const result = add(2, 3);
Note
TypeScript is a compile-time tool only. At runtime, your code is just JavaScript. TypeScript adds zero overhead to the running program.
High-Level Comparison

Feature

JavaScript

TypeScript

Type system

Dynamic (types checked at runtime)

Static (types checked at compile time)

Syntax

Standard ECMAScript

ECMAScript + type annotations

Runs directly in browser

Yes

No — must compile to JS first

IDE support

Good (inference-based)

Excellent (fully typed)

Compile step

None required

Required (tsc, esbuild, etc.)

Null safety

No (undefined is everywhere)

Yes (with strictNullChecks)

Interfaces / Generics

Not available

First-class features

Learning curve

Low

Moderate

Best for

Small scripts, prototypes

Large apps, teams, long-lived code

Type Annotations: The Most Visible Difference

The most obvious difference is that TypeScript lets you annotate variables, function parameters, and return types. These annotations are optional (TypeScript infers types when possible) but become increasingly valuable as your codebase grows.

JS
// JavaScript — no annotations, dynamic types
let count = 0;
count = 'oops';  // Valid JS — no error, but probably wrong

function double(n) {
  return n * 2;
}

double('5');   // Returns '55' (string * 2 === NaN? No — JS coerces)
               // Actually: '5' * 2 = 10 because * coerces to number
               // But: '5' + 2 = '52' — surprise!

TS
// TypeScript — explicit contracts
let count: number = 0;
count = 'oops';  // Error: Type 'string' is not assignable to type 'number'

function double(n: number): number {
  return n * 2;
}

double('5');   // Error: Argument of type 'string' is not assignable to parameter of type 'number'
Type Inference: Less Annotation Than You Think

TypeScript is smart enough to infer types without explicit annotations in many cases. You don't have to annotate everything — only the parts where the compiler needs a hint.

TS
// TypeScript infers the type from the assignment
let name = 'Alice';          // inferred: string
let age = 30;                // inferred: number
let active = true;           // inferred: boolean

// TypeScript infers the return type from the function body
function square(n: number) {
  return n * n;              // inferred return type: number
}

// TypeScript infers array element types
const scores = [95, 87, 92]; // inferred: number[]
scores.push('A');            // Error: Argument of type 'string' is not assignable to type 'number'
Tip
As a rule of thumb: let TypeScript infer types for local variables and return types. Always annotate function parameters and public API boundaries (exported functions, class methods, React props) explicitly.
Error Detection: Compile Time vs Runtime

This is the most practically significant difference between the two languages:

Error Type

JavaScript

TypeScript

Wrong argument type

Runtime error or silent bug

Compile-time error

Missing property access

Runtime: undefined

Compile-time error

Null/undefined dereference

Runtime: TypeError

Compile-time error (with strict)

Typo in property name

Silent undefined

Compile-time error

Missing case in switch

Silent fallthrough

Detectable with exhaustiveness check

Wrong number of arguments

Runtime silent

Compile-time error

TS
interface Product {
  id: number;
  name: string;
  price: number;
}

function displayProduct(product: Product) {
  // Error: Property 'titel' does not exist on type 'Product'.
  // Did you mean 'name'?
  console.log(product.titel);
}
Null Safety: A Critical Improvement

JavaScript's null and undefined are the source of more runtime errors than almost anything else. The infamous "Cannot read properties of undefined" error is a JavaScript rite of passage.

TypeScript's strictNullChecks option forces you to handle null and undefined explicitly:

JS
// JavaScript — null is silent
function getUser(id) {
  return database.find(id);  // might return null
}

const user = getUser(42);
console.log(user.name);  // TypeError at runtime if user is null

TS
// TypeScript with strictNullChecks
function getUser(id: number): User | null {
  return database.find(id) ?? null;
}

const user = getUser(42);

// Error: Object is possibly null
console.log(user.name);

// Correct: handle the null case explicitly
if (user !== null) {
  console.log(user.name);  // TypeScript knows user is User here
}

// Or use optional chaining
console.log(user?.name);  // string | undefined — safe
Interfaces and Structural Typing

TypeScript introduces interfaces — a way to describe the shape of an object. JavaScript has no equivalent built-in construct (class is not the same thing).

TypeScript uses structural typing: a type is compatible with another if it has at least the required properties, regardless of what it was declared as.

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

// This works even though we didn't say "implements Point"
function printPoint(p: Point) {
  console.log(`(${p.x}, ${p.y})`);
}

// Any object with x and y is compatible
const origin = { x: 0, y: 0 };
const fancy = { x: 10, y: 20, label: 'Home' };  // extra property is fine

printPoint(origin);  // OK
printPoint(fancy);   // OK — structural typing accepts extra properties
Enums: TypeScript-Only Feature

TypeScript adds enums, which have no direct equivalent in JavaScript (you'd normally use a const object or string union).

JS
// JavaScript pattern — manual enum simulation
const Direction = Object.freeze({
  Up: 'UP',
  Down: 'DOWN',
  Left: 'LEFT',
  Right: 'RIGHT',
});

// No compile-time protection
function move(dir) {
  // dir could be anything
}

TS
// TypeScript enum
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT',
}

function move(dir: Direction) {
  console.log(dir);
}

move(Direction.Up);   // OK
move('diagonal');     // Error: Argument of type '"diagonal"' is not assignable to type 'Direction'
Note
Many modern TypeScript style guides prefer a const union type over enums: type Direction = "UP" | "DOWN" | "LEFT" | "RIGHT". It produces no runtime code and works just as well for most use cases.
Generics: Writing Reusable Typed Code

Generics are a TypeScript feature that lets you write functions and classes that work with any type while still being type-safe. JavaScript has no equivalent.

JS
// JavaScript — works, but loses type information
function first(array) {
  return array[0];  // always returns 'any' — type is lost
}

const n = first([1, 2, 3]);    // we know it's a number, but JS doesn't
const s = first(['a', 'b']);   // same function, different types — JS can't tell

TS
// TypeScript generic — preserves type information
function first<T>(array: T[]): T | undefined {
  return array[0];
}

const n = first([1, 2, 3]);    // TypeScript knows: n is number | undefined
const s = first(['a', 'b']);   // TypeScript knows: s is string | undefined

n?.toFixed(2);    // OK — number method available
s?.toUpperCase(); // OK — string method available
Tooling and Developer Experience

TypeScript's language server gives editors information that plain JavaScript simply cannot provide:

IDE Feature

JavaScript

TypeScript

Autocomplete

Heuristic / partial

Comprehensive and accurate

Hover type info

Sometimes (inferred)

Always available

Go to definition

Works for local symbols

Works across the entire project

Rename symbol

Unreliable

Safe — checks all call sites

Unused variable detection

ESLint only

Built into the compiler

Inline error display

ESLint only

Compiler + ESLint

Build Pipeline Differences

JavaScript can run directly in browsers and Node.js without any transformation. TypeScript always needs a compilation step.

Text
JavaScript workflow:
  Write .js → Run directly in Node.js or browser

TypeScript workflow:
  Write .ts → tsc / esbuild / SWC → .js → Run in Node.js or browser

In practice, most modern JavaScript projects already have a build step (webpack, Vite, Rollup), so adding TypeScript to that pipeline is usually straightforward — often a one-line config change.

Ecosystem and Third-Party Libraries

A common concern about adopting TypeScript is whether third-party libraries support it. The situation has improved dramatically:

  • Many popular packages now ship built-in TypeScript definitions (React, Next.js, Express, Prisma, Zod)

  • The @types/* packages on npm provide community-maintained definitions for thousands of libraries

  • When a package ships no types and no @types/* exists, you can write local declarations or use any

  • The DefinitelyTyped repository contains type definitions for over 8,000 npm packages

Bash
# Install types for packages that don't include them
npm install --save-dev @types/node   # Node.js built-in types
npm install --save-dev @types/lodash # Lodash types
Which Should You Choose?

Situation

Recommendation

New project, any size

TypeScript — setup cost is low, benefits compound over time

Small script / quick prototype

JavaScript — no build step needed

Large existing JS codebase

Migrate gradually — allowJs: true in tsconfig

Team with TypeScript experience

TypeScript without hesitation

Team new to typed languages

Start with TypeScript basics, ramp up strictness over time

Library you will publish to npm

TypeScript — you generate .d.ts files for consumers

The Bottom Line

TypeScript and JavaScript are not competitors — TypeScript is JavaScript, with an optional type layer on top. You can start using TypeScript today without abandoning any of the JavaScript ecosystem you already know.

The question is not really "TypeScript or JavaScript?" — it is "do I want a compiler checking my work?". For any project that will be maintained, grown, or worked on by more than one person, the answer is almost always yes.

Success
TypeScript is not a different language — it is a better JavaScript. Every JavaScript skill you already have transfers directly.
Summary
  • TypeScript is a superset of JavaScript — all JS is valid TS

  • TypeScript adds type annotations, interfaces, enums, and generics that are erased at compile time

  • The key benefit is catching type errors at compile time rather than runtime

  • TypeScript inference means you annotate far less than you might expect

  • strictNullChecks eliminates the most common class of runtime errors

  • Editor support (autocomplete, hover docs, safe rename) is dramatically better with TypeScript

  • Adoption is gradual — you can mix JS and TS files in the same project