TypeScript vs JavaScript: Key Differences and When to Choose TypeScript
TypeScript has gone from a Microsoft experiment to the dominant choice for large-scale JavaScript projects. But JavaScript is still the right tool in many contexts. Understanding the trade-offs helps you make informed decisions β and gives you sharp answers in interviews.
What TypeScript Adds
TypeScript is a strict superset of JavaScript. Every valid JavaScript file is valid TypeScript. TypeScript adds:
- Static types β annotate variables, parameters, and return types
- Type inference β TypeScript figures out types from context
- Interfaces and type aliases β define the shape of objects
- Generics β reusable code that works across multiple types
- Enums β named constants
- Access modifiers β
public,private,protectedon class members - Non-null checks β catch potential
null/undefinederrors at compile time
TypeScript compiles to plain JavaScript β browsers and Node.js never see TypeScript directly.
Static Typing: The Core Difference
javascript// JavaScript -- no type information function add(a, b) { return a + b; } add(1, 2); // 3 -- correct add("1", 2); // "12" -- bug, no error add(1, null); // 1 -- bug, no error
typescript// TypeScript -- types enforced function add(a: number, b: number): number { return a + b; } add(1, 2); // 3 -- correct add("1", 2); // Error: Argument of type 'string' is not assignable to 'number' add(1, null); // Error: Argument of type 'null' is not assignable to 'number'
TypeScript catches these bugs before your code runs β at compile time, in your editor.
Type Inference
You do not have to annotate everything. TypeScript infers types from assignments and usage:
typescriptconst name = "Alice"; // inferred: string const age = 30; // inferred: number const active = true; // inferred: boolean const numbers = [1, 2, 3]; // inferred: number[] numbers.push("hello"); // Error: string is not assignable to number function double(n: number) { return n * 2; // return type inferred: number }
Good TypeScript code often has fewer explicit annotations than you might expect β inference does the heavy lifting.
Interfaces and Type Aliases
Define the shape of objects:
typescript// Interface interface User { id: number; name: string; email: string; role?: string; // optional field } // Type alias type Point = { x: number; y: number; }; // Using them function greetUser(user: User): string { return `Hello, ${user.name}`; } const user: User = { id: 1, name: "Alice", email: "alice@example.com" }; greetUser(user); // fine greetUser({ id: 1, name: "Alice" }); // Error: email is missing
Interface vs Type alias
Both define object shapes. The practical differences:
| Interface | Type alias | |
|---|---|---|
| Declaration merging | Yes | No |
| Extends | extends keyword | Intersection & |
| Union types | No | Yes |
| Computed properties | No | Yes |
Prefer interface for object shapes you might extend. Use type for unions, intersections, and utility types.
Union and Intersection Types
typescript// Union -- value can be one of several types type ID = string | number; type Status = "pending" | "active" | "inactive"; function formatId(id: ID): string { if (typeof id === "number") { return id.toString().padStart(8, "0"); } return id; } // Intersection -- value must satisfy all types type AdminUser = User & { permissions: string[] }; const admin: AdminUser = { id: 1, name: "Alice", email: "alice@example.com", permissions: ["read", "write", "delete"], };
Enums
typescriptenum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT", } function move(direction: Direction) { console.log(`Moving ${direction}`); } move(Direction.Up); // fine move("diagonal"); // Error: not assignable to Direction
Many TypeScript developers prefer string literal union types over enums β they compile away to nothing and behave more predictably:
typescripttype Direction = "UP" | "DOWN" | "LEFT" | "RIGHT";
Strict Mode
Enable strict mode in tsconfig.json for maximum safety:
json{ "compilerOptions": { "strict": true } }
Strict mode enables strictNullChecks (no implicit null/undefined), noImplicitAny, and several other checks. Always use strict mode in new projects.
Tooling Benefits
TypeScript's type information powers your development tools:
- Autocomplete β your editor knows every method and property available
- Inline documentation β hover to see types, JSDoc, and parameter info
- Refactoring β rename a function or property and every reference updates safely
- Error highlighting β bugs appear as red squiggles before you run anything
- Go to definition β jump to the source of any function or type instantly
These benefits are most valuable in large codebases where you cannot hold everything in your head at once.
TypeScript in Practice
React with TypeScript
typescriptinterface ButtonProps { label: string; onClick: () => void; variant?: "primary" | "secondary" | "danger"; disabled?: boolean; } const Button: React.FC<ButtonProps> = ({ label, onClick, variant = "primary", disabled = false, }) => ( <button className={`btn btn-${variant}`} onClick={onClick} disabled={disabled} > {label} </button> );
Express with TypeScript
typescriptimport express, { Request, Response } from "express"; interface CreateUserBody { name: string; email: string; } app.post("/users", async (req: Request<{}, {}, CreateUserBody>, res: Response) => { const { name, email } = req.body; // fully typed const user = await userService.create({ name, email }); res.status(201).json(user); });
When to Choose TypeScript
Use TypeScript when:
- Building a large codebase or a long-lived project
- Working in a team β types serve as documentation and prevent misunderstandings
- Building a library or public API β types tell consumers exactly what to pass
- Your codebase has complex data structures and business logic
- You want the best IDE experience
JavaScript may be fine when:
- Building a small script or tool
- Rapid prototyping where flexibility matters more than safety
- Working on a project where the TypeScript learning curve is a blocker
- Very small codebases where the overhead of compilation is not worth it
Migration Strategy
Migrating from JavaScript to TypeScript does not have to be all-at-once:
- Add a
tsconfig.jsonwith"allowJs": trueβ your JS files still work - Rename files from
.jsto.tsone at a time - Fix type errors as you go β start with
"strict": false, enable later - Add types to the most-used functions first
Practice TypeScript on Froquiz
TypeScript is now expected at most mid-level to senior frontend and backend roles. Test your JavaScript and TypeScript knowledge on Froquiz across all difficulty levels.
Summary
- TypeScript adds static typing, interfaces, generics, and enums to JavaScript
- Types are checked at compile time β bugs caught before runtime
- Type inference means you do not annotate everything explicitly
- Strict mode enables
strictNullChecksand other safety checks β always use it - Tooling (autocomplete, refactoring, inline docs) is the most immediately felt benefit
- Use TypeScript for large, long-lived, or team projects
- Migration can be gradual β start with
allowJs, rename files incrementally