TypeScript Basics
Master static type checking, primitive annotations, type inference, union types, narrowing, interfaces vs type aliases, and how to write robust, error-free modern code.
Introduction to Type Safety
JavaScript is dynamically typed: variables can hold any type, and types can mutate arbitrarily during execution. While flexible for small scripts, dynamic typing in large applications frequently leads to runtime crashes like TypeError: Cannot read properties of undefined.
TypeScript introduces a compile-time static type system that inspects your code before it ever executes in the browser or Node.js runtime, catching bugs during development while providing autocompletion and safe refactoring.
What Is TypeScript?
TypeScript is an open-source programming language developed by Microsoft. It is a typed superset of JavaScript:
Every valid JavaScript program is already a valid TypeScript program. TypeScript simply layers static types on top of JavaScript.
TypeScript vs JavaScript
| Feature | JavaScript | TypeScript |
|---|---|---|
| Typing | Dynamic (evaluated at runtime) | Static (checked at compile time) |
| Error Detection | During runtime execution in browser/server | Instantly inside editor / at build time |
| Tooling & Autocomplete | Basic inference | IntelliSense, automated refactoring, type docs |
| Execution | Directly executed by browsers and Node.js | Compiled to pure JavaScript before execution |
How TypeScript Works
TypeScript operates via a two-phase process:
Type Checking
The TypeScript compiler (tsc) verifies all types, interfaces, and function signatures. If any type contract is violated, compilation fails with clear error messages.
Type Erasure & Transpilation
All type annotations and interfaces are completely removed. The output is clean, standard JavaScript that any browser or engine can run with zero overhead.
Basic Primitive Types
TypeScript supports the foundational JavaScript primitives:
let developerName: string = "Sandeep"; let yearsExperience: number = 6; let isSenior: boolean = true; let middleName: null = null; let secondaryEmail: undefined = undefined;
Type Annotations
Type annotations use the : type syntax to explicitly declare what type a variable, function parameter, or function return value must be.
function calculateTotal(price: number, taxRate: number): number {
return price + (price * taxRate);
}Type Inference
You do not need to annotate every single variable. When you assign an initial value, TypeScript automatically infers the type.
Writing let count = 0; is cleaner than let count: number = 0; because TypeScript already knows count is a number. Reserve explicit annotations for function signatures and uninitialized variables.
Arrays and Objects
// Typed Arrays
let skills: string[] = ["React", "TypeScript", "Next.js"];
let scores: Array<number> = [98, 85, 92];
// Object Types with Optional Properties
let user: {
id: string;
name: string;
age?: number; // Optional property
} = {
id: "usr_101",
name: "Alex"
};Union Types
Union types (|) allow a variable or parameter to accept one of multiple types:
type Status = "idle" | "loading" | "success" | "error"; let currentStatus: Status = "loading"; let identifier: string | number = 402; identifier = "AB-901"; // Also valid!
Type Narrowing
Type Narrowing is the process of refining a broad type (like string | number) into a specific type within conditional branches.
typeof
if (typeof val === "string") — Narrows primitives (string, number, boolean).
in Operator
if ("role" in user) — Checks if a property exists on an object.
instanceof
if (err instanceof Error) — Narrows class instances and errors.
Type Aliases vs. Interfaces
type Person = { ... }
Can model object shapes, primitives, union types, tuples, and function signatures. Cannot be reopened with declaration merging.
interface Person { ... }
Specifically designed for object shapes and class contracts. Supports extends and declaration merging.
any vs. unknown (Crucial Distinction)
Using any tells TypeScript to completely turn off type checking for that value. It re-introduces runtime bugs and breaks autocompletion. Official TypeScript guidance recommends avoiding any.
When dealing with untrusted input (like API responses or dynamic JSON parsing), use unknown instead. unknown forces you to safely narrow the type before doing anything with it:
let rawData: unknown = JSON.parse('{"name": "Sandeep"}');
// TypeScript blocks unsafe access:
// rawData.name; -> ERROR: Object is of type 'unknown'.
// Safe Narrowing:
if (typeof rawData === 'object' && rawData !== null && 'name' in rawData) {
console.log((rawData as { name: string }).name); // Safe!
}TypeScript Best Practices & Guidelines
- Enable
"strict": truein yourtsconfig.jsonfor full safety. - Avoid
any. Useunknownwhen the type is truly uncertain. - Rely on Type Inference for simple local variable assignments.
- Use Type Guards (
typeof,in,instanceof) to narrow union types. - Define explicit return types on public service functions for clear API contracts.
🔥 Live Interactive — TypeScript Type Checker Playground
Inspect real compiler diagnostics, observe type mismatches, and fix type errors in real time.
Type 'string' is not assignable to type 'number'.
The variable 'age' was explicitly declared as type 'number', but was assigned a string '"25"'. TypeScript blocks this assignment at compile time.