|$ curl https://forge-ai.dev/api/markdown?path=docs/typescript
$cat docs/typescript-—-0-to-hero.md
updated Recently·45 min read·published

TypeScript — 0 to Hero

TypeScriptBeginner to Advanced🎯Free Tools
Introduction

TypeScript is a statically typed superset of JavaScript developed by Microsoft. It adds optional type annotations, interfaces, generics, and other type-level constructs to JavaScript, catching errors at compile time rather than runtime. TypeScript compiles down to plain JavaScript — any valid JS is valid TS.

Created by Anders Hejlsberg (also behind C# and Delphi), TypeScript was first released in 2012. It has since become the dominant language for large-scale JavaScript applications, used by Angular, React, Vue, and virtually every major framework.

TypeScript's type system is structural, not nominal — types are compatible based on their shape, not their name. This makes it flexible while still catching real bugs. The type system is also Turing-complete, enabling advanced patterns like conditional types, mapped types, and template literal types.

Why TypeScript?

JavaScript is dynamically typed — type errors surface at runtime, often in production. TypeScript adds a compile-time type checker that catches these errors before code ships. Beyond safety, TypeScript provides better IDE support (autocomplete, go-to-definition, refactoring), self-documenting code, and easier onboarding for large teams.

why-ts.js
TypeScript
1// JavaScript — this "works" until runtime
2function add(a, b) {
3 return a + b;
4}
5add(1, "2"); // "12" — silent string concatenation, probably a bug
6
7// TypeScript — this catches the error at compile time
8function add(a: number, b: number): number {
9 return a + b;
10}
11add(1, "2"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'
📝

note

TypeScript does not change how your code runs. It only adds compile-time checks. The output is always valid JavaScript.
Setting Up

Getting started with TypeScript requires the TypeScript compiler (tsc) and a configuration file. Most projects use npm or yarn to manage the dependency.

terminal
Bash
1# Install TypeScript globally or as a dev dependency
2npm install -g typescript
3
4# Or per project (recommended)
5npm init -y
6npm install -D typescript
7
8# Verify installation
9tsc --version # e.g., 5.4.5
10
11# Compile a file
12tsc hello.ts # outputs hello.js
13
14# Watch mode — recompile on changes
15tsc --watch
16
17# Initialize tsconfig.json (recommended for all projects)
18tsc --init
hello.ts
TypeScript
1// hello.ts
2function greet(name: string): string {
3 return `Hello, ${name}!`;
4}
5
6const message = greet("TypeScript");
7console.log(message); // Hello, TypeScript!

best practice

Always use tsconfig.json instead of passing flags to tsc. It documents your project's configuration and is the standard for all TypeScript projects.
Type System Overview

TypeScript's type system is one of the most powerful in any mainstream language. It supports structural typing, type inference, generics, conditional types, and more. Here are the core concepts:

CategoryExamplesPurpose
Primitivesstring, number, boolean, null, undefinedBasic value types
Complexarray, tuple, object, enumCompound data structures
Specialany, unknown, void, never, neverControl type checking behavior
Composedunion, intersection, type aliasCombine types
Structuralinterface, type, classDefine shapes
type_overview.ts
TypeScript
1// TypeScript infers types when possible
2let x = 42; // inferred: number
3let s = "hello"; // inferred: string
4let arr = [1, 2, 3]; // inferred: number[]
5
6// Explicit annotations when needed
7let name: string = "Alice";
8let age: number = 30;
9let active: boolean = true;
10
11// Type inference is usually enough
12function multiply(a: number, b: number) {
13 return a * b; // return type inferred as number
14}
Key Features

TypeScript adds several features on top of JavaScript. Here are the most impactful ones:

features.ts
TypeScript
1// Interfaces — define object shapes
2interface User {
3 name: string;
4 age: number;
5 email?: string; // optional
6}
7
8// Generics — reusable type-safe components
9function first<T>(items: T[]): T | undefined {
10 return items[0];
11}
12
13const num = first([1, 2, 3]); // inferred: number | undefined
14const str = first(["a", "b"]); // inferred: string | undefined
15
16// Type narrowing — TypeScript knows the type inside branches
17function process(value: string | number) {
18 if (typeof value === "string") {
19 return value.toUpperCase(); // TS knows: string
20 }
21 return value.toFixed(2); // TS knows: number
22}
23
24// Enums — named constants
25enum Direction {
26 Up = "UP",
27 Down = "DOWN",
28 Left = "LEFT",
29 Right = "RIGHT",
30}
31
32const move = Direction.Up; // "UP"

info

TypeScript's type system is structural — two types are compatible if they have the same shape, regardless of name. This means you rarely need to explicitly declare implements relationships.
Getting Started with tsc

The tsc compiler reads your TypeScript files and emits JavaScript. The tsconfig.json file controls compilation settings, including target ECMAScript version, module system, strictness, and file inclusion.

tsconfig.json
JSON
1{
2 "compilerOptions": {
3 "target": "ES2022",
4 "module": "ESNext",
5 "moduleResolution": "bundler",
6 "strict": true,
7 "esModuleInterop": true,
8 "skipLibCheck": true,
9 "outDir": "./dist",
10 "rootDir": "./src",
11 "declaration": true,
12 "sourceMap": true
13 },
14 "include": ["src/**/*"],
15 "exclude": ["node_modules", "dist"]
16}
FlagWhat It Does
strictEnables all strict type-checking options
targetECMAScript version for output (ES5, ES2020, ES2022)
moduleModule system (CommonJS, ESNext, NodeNext)
declarationEmit .d.ts declaration files
sourceMapGenerate source maps for debugging
noUncheckedIndexedAccessArray/object index access returns T | undefined

best practice

For new projects, always start with strict: true. You can relax individual flags if needed, but strict mode catches the most bugs from the start.
Next Steps

This guide covers the essentials. From here, dive into the specific topics below to build a deep understanding of TypeScript's type system and features.

roadmap.ts
TypeScript
1// The journey ahead:
2// 1. Types & Annotations — primitives, arrays, tuples, unions
3// 2. Type Inference — how TS figures out types for you
4// 3. Literals & Unions — literal types, discriminated unions
5// 4. Enums — named constants and reverse mappings
6// 5. Function Types — signatures, overloads, callbacks
7// 6. Interfaces & Classes — OOP patterns in TypeScript
8// 7. Generics — reusable type-safe components
9// 8. Utility Types — Partial, Pick, Omit, Record, etc.
10// 9. Advanced Types — conditional, mapped, template literal
11// 10. tsconfig Deep Dive — compiler configuration mastery
Structural Typing Deep Dive

TypeScript compares shapes, not names. Two types with the same properties are compatible even if declared separately. This is powerful — and surprising if you come from nominal languages like Java or C#.

structural.ts
TypeScript
1type Point2D = { x: number; y: number };
2type Vec2 = { x: number; y: number };
3const p: Point2D = { x: 1, y: 2 };
4const v: Vec2 = p; // OK — same shape
5
6class User { constructor(public name: string) {} }
7class Person { constructor(public name: string) {} }
8const u: User = new Person("Ada"); // OK at compile time

warning

Excess property checks apply to fresh object literals assigned to a target type — a common source of confusion when spreading or reusing variables.
untitled.typescript
TypeScript
1type Options = { debug?: boolean };
2const o = { debug: true, verbose: true };
3const a: Options = { debug: true, verbose: true }; // Error — excess verbose
4const b: Options = o; // OK — not a fresh literal
Everyday Patterns You Will Need

A compact catalog of patterns that show up in real apps. Each links to a deeper page.

everyday.ts
TypeScript
1// 1) Optional fields + defaults
2type Cfg = { port?: number };
3function start({ port = 3000 }: Cfg = {}) {}
4
5// 2) Discriminated union state
6type Remote<T> =
7 | { status: "idle" }
8 | { status: "loading" }
9 | { status: "success"; data: T }
10 | { status: "error"; error: Error };
11
12// 3) keyof + indexed access
13function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
14 return obj[key];
15}
16
17// 4) const assertion for literal unions
18const MODES = ["dev", "prod"] as const;
19type Mode = (typeof MODES)[number];
20
21// 5) satisfies for configs
22const theme = { primary: "#3178C6" } as const satisfies Record<string, `#${string}`>;

Go deeper: narrowing, satisfies, generics, types reference.

Tooling Quickstart
untitled.bash
Bash
1npm i -D typescript
2npx tsc --init
3# enable "strict": true in tsconfig.json
4npx tsc --noEmit
tsconfig.recommended.json
JSON
1{
2 "compilerOptions": {
3 "target": "ES2022",
4 "module": "NodeNext",
5 "moduleResolution": "NodeNext",
6 "strict": true,
7 "noUncheckedIndexedAccess": true,
8 "skipLibCheck": true,
9 "esModuleInterop": true,
10 "forceConsistentCasingInFileNames": true
11 },
12 "include": ["src"]
13}

best practice

Read the Strict Mode guide before relaxing flags. Prefer fixing types over disabling checks.
Coming from JavaScript

Valid JavaScript is valid TypeScript. Start by renaming .js.ts, turning on allowJs, then ratcheting strictness. Add annotations where inference is insufficient — not everywhere.

untitled.typescript
TypeScript
1// JS habit
2function add(a, b) { return a + b; }
3
4// TS — annotate params; return type often inferred
5function addTs(a: number, b: number) {
6 return a + b; // number
7}
8
9// Untrusted input
10function parse(raw: unknown) {
11 if (typeof raw !== "string") throw new Error("string");
12 return raw.toUpperCase();
13}

Full migration playbook: /docs/typescript/strict. Runtime validation: /docs/typescript/zod.

Additional Depth — index (1)

Extra worked material to reinforce index. Keep strict: true enabled while experimenting.

index-0.ts
TypeScript
1// Depth set 1 for index
2type Id0 = string;
3type Box0<T> = { value: T };
4function wrap0<T>(value: T): Box0<T> {
5 return { value };
6}
7const sample0 = wrap0({ ok: true as const, n: 0 });
8type Sample0 = typeof sample0;
9
10type Keys0 = keyof Sample0;
11type Val0 = Sample0["value"];
12
13type Union0 = "a" | "b" | "c";
14type Upper0 = Uppercase<Union0>;
15
16type Fn0 = (x: number) => string;
17type Ret0 = ReturnType<Fn0>;
18type Params0 = Parameters<Fn0>;
19
20type PartialUser0 = Partial<{ id: string; name: string }>;
21type RequiredUser0 = Required<PartialUser0>;
22
📝

note

If this compiles, try breaking it — remove a field, widen a literal, or add null — and read the error.
CheckPass criteria
InferenceHover types match expectations
Strict nullNo implicit undefined holes
RefactorRename propagates safely
Additional Depth — index (2)

Extra worked material to reinforce index. Keep strict: true enabled while experimenting.

index-1.ts
TypeScript
1// Depth set 2 for index
2type Id1 = string;
3type Box1<T> = { value: T };
4function wrap1<T>(value: T): Box1<T> {
5 return { value };
6}
7const sample1 = wrap1({ ok: true as const, n: 1 });
8type Sample1 = typeof sample1;
9
10type Keys1 = keyof Sample1;
11type Val1 = Sample1["value"];
12
13type Union1 = "a" | "b" | "c";
14type Upper1 = Uppercase<Union1>;
15
16type Fn1 = (x: number) => string;
17type Ret1 = ReturnType<Fn1>;
18type Params1 = Parameters<Fn1>;
19
20type PartialUser1 = Partial<{ id: string; name: string }>;
21type RequiredUser1 = Required<PartialUser1>;
22
📝

note

If this compiles, try breaking it — remove a field, widen a literal, or add null — and read the error.
CheckPass criteria
InferenceHover types match expectations
Strict nullNo implicit undefined holes
RefactorRename propagates safely
Additional Depth — index (3)

Extra worked material to reinforce index. Keep strict: true enabled while experimenting.

index-2.ts
TypeScript
1// Depth set 3 for index
2type Id2 = string;
3type Box2<T> = { value: T };
4function wrap2<T>(value: T): Box2<T> {
5 return { value };
6}
7const sample2 = wrap2({ ok: true as const, n: 2 });
8type Sample2 = typeof sample2;
9
10type Keys2 = keyof Sample2;
11type Val2 = Sample2["value"];
12
13type Union2 = "a" | "b" | "c";
14type Upper2 = Uppercase<Union2>;
15
16type Fn2 = (x: number) => string;
17type Ret2 = ReturnType<Fn2>;
18type Params2 = Parameters<Fn2>;
19
20type PartialUser2 = Partial<{ id: string; name: string }>;
21type RequiredUser2 = Required<PartialUser2>;
22
📝

note

If this compiles, try breaking it — remove a field, widen a literal, or add null — and read the error.
CheckPass criteria
InferenceHover types match expectations
Strict nullNo implicit undefined holes
RefactorRename propagates safely
Additional Depth — index (4)

Extra worked material to reinforce index. Keep strict: true enabled while experimenting.

index-3.ts
TypeScript
1// Depth set 4 for index
2type Id3 = string;
3type Box3<T> = { value: T };
4function wrap3<T>(value: T): Box3<T> {
5 return { value };
6}
7const sample3 = wrap3({ ok: true as const, n: 3 });
8type Sample3 = typeof sample3;
9
10type Keys3 = keyof Sample3;
11type Val3 = Sample3["value"];
12
13type Union3 = "a" | "b" | "c";
14type Upper3 = Uppercase<Union3>;
15
16type Fn3 = (x: number) => string;
17type Ret3 = ReturnType<Fn3>;
18type Params3 = Parameters<Fn3>;
19
20type PartialUser3 = Partial<{ id: string; name: string }>;
21type RequiredUser3 = Required<PartialUser3>;
22
📝

note

If this compiles, try breaking it — remove a field, widen a literal, or add null — and read the error.
CheckPass criteria
InferenceHover types match expectations
Strict nullNo implicit undefined holes
RefactorRename propagates safely
$Blueprint — Engineering Documentation·Section ID: TS-HERO·Revision: 1.0

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.