TypeScript in 15 Minutes for JavaScript Developers Who Hate TypeScript

Published: (December 15, 2025 at 08:06 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

TypeScript vs JavaScript

It’s JavaScript with intent, discipline, and receipts.

At its core, TypeScript is just JavaScript plus types and generics — nothing more, nothing magical. It doesn’t add new runtime behavior; it simply makes sure you don’t lie to yourself (or your teammates) while writing code.

If you already know JavaScript, this is the fastest way to understand what actually matters in TypeScript.

JavaScript vs. TypeScript: The Truth About Types

TypeScript does NOT introduce new primitive data types.
Everything you already know still exists:

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol
  • bigint

TypeScript’s job is not invention — it’s enforcement.

  • JavaScript: “You’ll figure it out at runtime.”
  • TypeScript: “Prove it before you run it.”

Methods Are the Same — Safety Is Not

All your familiar methods still exist:

  • Stringsconcat, slice, charAt
  • Arraysmap, filter, reduce, find

The difference is that TypeScript knows what you’re allowed to call.

let nums: number[] = [1, 2, 3];
nums.map(n => n * 2);               // ✅ OK
nums.map(n => n.toUpperCase());     // ❌ Error – caught at compile time

JavaScript would let the second line fail at runtime; TypeScript stops it at compile time. That’s the entire value proposition.

Type Annotations vs. Type Inference

You can explicitly annotate:

let name: string = "Ram";

But TypeScript is smarter than that:

let name = "Ram"; // inferred as `string`

TypeScript tracks types automatically. This is called type inference, and it’s why declaring variables in one place and mutating them elsewhere is usually a bad idea.

The any Trap (Don’t Do This)

A lot of people use any to “escape” TypeScript, which defeats the purpose.

let value: any = 10;
value.toUpperCase(); // ✅ TS allows it, but runtime explodes

That’s why noImplicitAny exists in tsconfig.json. If you’re using any everywhere, you’re essentially writing plain JavaScript with extra steps.

Functions: Parameters AND Returns Matter

TypeScript isn’t just about inputs — outputs matter too.

function addTwo(num: number): number {
  return num + 2;
}

Without the return type, the following would still compile, but it would be a bug:

function addTwo(num: number) {
  return "hello"; // ❌ Type error if return type is declared
}

TypeScript lets you lock both sides of the contract.

Special Return Types

  • void → function performs side effects only
  • never → function never returns (throws or crashes)
function handleError(msg: string): void {
  console.log(msg);
}

function crash(msg: string): never {
  throw new Error(msg);
}

Why Return Types Matter in Teams

A well‑typed function tells a story without reading its body:

function getCourse(): { title: string; price: number } {
  return { title: "TypeScript Mastery", price: 499 };
}

Anyone can instantly see:

  • What it returns
  • The exact structure
  • What’s mandatory

That’s not just syntax; it’s team communication.

Type Aliases: Naming Shapes

Type aliases let you give intent a name.

type User = {
  name: string;
  email: string;
  isActive: boolean;
};

JavaScript says “trust me.”
TypeScript says “prove it.”

readonly, Optional, and Union Types

readonly

readonly _id: string;

You can read it, but you can’t mutate it.

Optional Properties

creditCard?: number;

The property might exist or not; TypeScript forces you to check.

Union Types

let id: number | string;

Used heavily in:

  • Role‑Based Access Control (RBAC)
  • API responses
  • Conditional flows

Tuples: Controlled Chaos

Tuples are ordered, typed arrays:

let user: [string, number, boolean];
  • They exist only in TypeScript – the compiled JavaScript doesn’t care.
  • push() still works (but use it sparingly).
  • Use them only when the order truly matters.

Enums: Named Constants

const enum SeatChoice {
  aisle = 10,
  middle,
  window
}
  • Readable and predictable.
  • Zero runtime ambiguity because the enum is inlined at compile time.

Interfaces vs. Types (Short Version)

Interfaces

  • Extendable.
  • Work beautifully with classes – enforce OOP contracts.
interface IUser {
  email: string;
  startTrial(): string;
}
  • Interfaces can be reopened (declaration merging).
  • Types cannot be reopened.

Types

  • Useful for unions, intersections, and primitive aliases.
  • Not merge‑able, but great for one‑off shapes.

Classes, Constructors, and Access Modifiers

TypeScript makes OOP less painful:

class User {
  constructor(
    public email: string,
    public name: string,
    private userId: string
  ) {}
}

Access Modifiers

ModifierVisibility
publicAnywhere
privateInside the class only
protectedInside the class and its subclasses

These modifiers help you stop “access chaos” in real systems.

Interfaces + Classes = Structural Integrity

Interface (shape)

interface TakePhoto {
  cameraMode: string;
  filter: string;
}

Class (behavior)

class Camera implements TakePhoto {
  cameraMode = "auto";
  filter = "none";

  snap() { /* … */ }
}

Interfaces define what must exist; classes provide the implementation.
This pattern scales incredibly well in backend (and frontend) systems.

Abstract Classes: Intent Without Instantiation

abstract class TakePhoto {
  abstract getSepia(): void;
}
  • Cannot be instantiated directly.
  • Sub‑classes must implement the abstract members, guaranteeing required behavior.

Generics: The Real Power Move

Generics let you write logic once – safely:

function identity<T>(val: T): T {
  return val;
}

Common places where generics shine:

  • API client wrappers
  • Repository patterns
  • Response models

They’re not an OOP replacement; they’re an evolution of type safety.

Type Narrowing: Runtime Safety

TypeScript never “guesses” – you narrow types explicitly with:

  • typeof checks
  • instanceof checks
  • The in operator

These techniques keep the compiler happy while ensuring correct runtime behavior without reflection.

Discriminated Unions (Cleanest Pattern)

interface CreditCard {
  paymentType: "card";
  cardNumber: string;
}

interface PayPal {
  paymentType: "paypal";
  email: string;
}

type PaymentMethod = CreditCard | PayPal;
  • A single discriminant field (paymentType) gives zero ambiguity.
  • Perfect for switch statements or exhaustive if checks.

Conclusion

  • TypeScript doesn’t slow you down – unclear code does.
  • It makes ambiguity illegal, forcing you to write explicit, maintainable, and type‑safe code.
Back to Blog

Related posts

Read more »

JSDoc is TypeScript

In May 2023 an internal refactoring PRhttps://github.com/sveltejs/svelte/pull/8569 from the Svelte repo made it to the front page of Hacker News. The superficia...