TypeScript Survival Guide

Favorite typescript videos

001 — TypeScript Tutorial for Beginners (setup, tsconfig, type fundamentals)

Full companion narrative for TypeScript Tutorial for Beginners — environment setup, first program, tsconfig, debugging, then primitives, arrays, tuples, enums, functions, objects, aliases, unions, intersections, narrowing, and nullables

Video: 001 · TypeScript Tutorial for Beginners · watch · 1:04:28 · Ultimate TypeScript course, first-hour beginners lesson (Mosh Hamedani)

Source: this is a written companion to the video. Part of the Favorite TypeScript Videos curated path, modules 00 (start) and 01 (fundamentals). Two parts: Part A is tooling and workflow (setup, first program, tsconfig, debugging); Part B is type fundamentals. All code below is reconstructed from the audio transcript except the literal-types frame noted at the end.

Tooling: setup, first program, and debugging

course premise and prerequisites

No TypeScript knowledge required. Required: basic JavaScript (variables and constants, arrays, objects, functions, arrow functions, destructuring). Method: watch every lesson in order, take written notes (keywords suffice), do the exercises at the end of each section.

what TypeScript is

TypeScript is a language built on top of JavaScript (a superset): every JavaScript file is a valid TypeScript file, plus extra features. Created at Microsoft to address JavaScript shortcomings for large-scale applications.

The headline feature is static typing: variable types are known at compile time. Statically typed languages (C++, C#, Java) fix a variable’s type at declaration; dynamically typed languages (JavaScript, Python, Ruby) resolve types at runtime and allow them to change.

let age: number = 20;
age = "twenty"; // ERROR: Type 'string' is not assignable to type 'number'

Beyond type checking: editors use known types for code completion and refactoring, and TypeScript lets you use future-JavaScript features before browsers implement them (they get compiled down).

Drawbacks (both stated explicitly):

  1. Compilation step (transpilation) — browsers do not run TypeScript, so tsc must translate it to JavaScript.
  2. More discipline required — slower for quick scripts; pays off on large multi-developer projects. Simple apps can stay vanilla JavaScript.

dev environment setup

Prerequisite: Node.js (from nodejs.org), for npm. Install the compiler globally so tsc works in every folder:

npm i -g typescript
tsc -v   # verify, e.g. 4.6.3 in the video; newer versions are fine

Editor: VS Code (code.visualstudio.com). Any editor works, but the course shortcuts assume VS Code. (code . opens a folder; drag-and-drop works if the code command is not on PATH.)

first TypeScript program

Project: new folder (e.g. hello-world on Desktop), opened in VS Code. New file index.ts — every TypeScript file uses the .ts extension. Plain JavaScript is valid TypeScript:

console.log("hello-world");

Compile with the embedded terminal (View > Terminal, Ctrl+backtick on Mac):

tsc index.ts   # produces index.js next to it

TypeScript demo — annotate with colon syntax:

let age: number = 20;
age = "a string"; // ERROR: Type 'string' is not assignable to type 'number'

Delete the bad line, recompile. Key observation: the emitted index.js uses var instead of let and drops the annotation. Default tsc targets ES5 (ECMAScript 5, implemented in browsers for over a decade), and type annotations exist only for the compiler — they never appear in the output.

configuring the compiler (tsconfig.json)

tsc --init   # creates tsconfig.json, mostly commented-out settings

Only a handful of settings matter; nobody memorizes all of them, and each has an inline description. Settings used in this video:

settingeffect
targetJS version emitted (e.g. ES2016+ keeps let)
modulemodule system (e.g. commonjs)
outDiremit folder (dist)
rootDirsources folder (src)
include / excludewhich files compile
sourceMapemit .js.map files for debugging
strict (+ noImplicitAny, strictNullChecks)type-checking strictness
noImplicitReturnsevery code path must return
noUnusedLocals / noUnusedParametersflag dead variables

Workflow: set outDir to dist, then tsc alone compiles every file in the project into dist/.

debugging in VS Code

  1. Enable sourceMap in tsconfig — the .js.map file records how each TypeScript line maps to generated JavaScript (machine-readable, not for humans). Recompile to produce index.js.map.
  2. Debug panel > “create a launch.json file”.
  3. Set breakpoints, press F5, step with F10 (step over), watch variables (e.g. age changing to 30) line by line.

Type fundamentals: primitives through nullables

JavaScript built-ins: number, string, boolean, null, undefined, object. TypeScript adds: any, unknown, never, enum, tuple (covered: any, enum, tuple here).

primitives and inference

let sales: number = 123_456_789; // underscores separate digits, readability only
let course: string = "TypeScript";
let isPublished: boolean = true;

Annotation is often unnecessary: the compiler infers the type from the initializer. Hovering sales after removing : number still shows number. Takeaway: annotate when declaring without initializing; otherwise let inference do it.

the any type (and why to avoid it)

An uninitialized declaration falls back to any:

let level; // implicitly any
level = 1;
level = "a"; // allowed - type safety lost

any is the escape hatch for converting JS projects where a real type is not yet known:

function render(document: any) {
	console.log(document);
}

Without the annotation, noImplicitAny (on via strict) errors: “parameter ‘document’ implicitly has an ‘any’ type”. The “nuclear option” — turning noImplicitAny off in tsconfig — silences this project-wide, but the video explicitly recommends against it: without type checking there is no point using TypeScript. Rule: avoid any wherever possible.

arrays

JavaScript arrays are dynamic (mixed element types allowed). TypeScript narrows them:

let numbers: number[] = [1, 2, "3"]; // ERROR at compile time, fix immediately
let numbers2 = [1, 2, 3]; // annotation unneeded, inferred number[]
let empty: number[] = []; // REQUIRED: [] alone infers any[]

Benefit beyond errors: IntelliSense. numbers.forEach(n => n.) offers all number methods because the editor knows n is a number — no equivalent in plain JavaScript.

tuples

A fixed-length array where each position has its own type. Use for pairs:

let user: [number, string] = [1, "Mosh"];
user = [1, "Mosh", true]; // ERROR: only 2 elements allowed
user = ["Mosh", 1]; // ERROR: order and types fixed

Index access is typed: user[0]. offers number methods, user[1]. offers string methods. Caveat: internally tuples compile to plain JavaScript arrays, so some invalid operations (e.g. pushing a third element) slip past the compiler — know the limitation.

enums

A list of related constants:

enum Size {
	Small,
	Medium,
	Large,
} // 0, 1, 2 by default
let mySize: Size = Size.Medium;

Assign explicit values to override (Small = 1, or strings). Prefer const enum: the compiler emits optimized code (a single assignment line) instead of the verbose generated lookup object. Logging an enum member prints its value (e.g. 2).

const enum Size {
	Small = 1,
	Medium,
	Large,
}

functions

Always annotate parameters and return types; the compiler then enforces arity (exactly the declared arguments, no more, no fewer):

function calculateTax(income: number, taxYear: number): number {
	if (taxYear < 2022) return income * 1.2;
	return income * 1.3;
}
  • Optional parameter: taxYear?: number (must come after required ones).
  • Default value: taxYear: number = 2022 (callers may omit it).
  • noImplicitReturns: flags paths that fall through without returning — fix by annotating the return type and covering every branch.
  • noUnusedLocals / noUnusedParameters: unused declarations get “declared but never read” warnings — delete them.

objects

Inline shape annotation with { }:

let employee: { id: number; name: string } = { id: 1, name: "Mosh" };
  • Missing property at initialization errors (“property ’name’ is missing”).
  • Cannot assign null/undefined (strict null checks, next section).
  • name?: string makes a property optional — but avoid it when the concept demands the field (an employee without a name makes no sense; use '').
  • readonly id: number prevents reassignment after creation.

Problems with inline annotations: shape duplication across objects (violates DRY), inconsistent shapes, harder reading. Fix: type aliases.

type aliases

One named place for a shape, PascalCase by convention:

type Employee = {
	readonly id: number;
	name: string;
};

let employee: Employee = { id: 1, name: "Mosh" };

Also aliases for primitives/unions: type Quantity = 50 | 100;

src/index.ts showing literal types Quantity and Metric

union types

A parameter or variable accepting multiple types, joined with |:

function kgToLbs(weight: number | string): number {
	/* narrowing inside */
}

Used for nullable parameters too: name: string | null, extended to string | null | undefined when both are valid.

intersection types

Combine separate types with &; the result must satisfy all members:

type Draggable = { drag: () => void };
type Resizable = { resize: () => void };
type UIWidget = Draggable & Resizable;

let textBox: UIWidget = {
	drag: () => {},
	resize: () => {},
};

narrowing

Technique: narrow a union to one type before using type-specific members:

if (typeof weight === "number") return weight * 2.2;
else return parseInt(weight) * 2.2;

typeof guards (plus instanceof for objects) let the compiler treat the value as the narrowed type inside each branch.

nullable types and optional chaining

strictNullChecks (on via strict) forbids null/undefined where not declared — never turn it off. Pattern for possibly-absent values:

type Customer = { birthday?: Date };

function getCustomer(id: number): Customer | null {
	return id === 0 ? null : { birthday: new Date() };
}

let customer = getCustomer(1);
console.log(customer?.birthday?.getFullYear()); // only runs if present

Operators:

  • ?. optional property access: customer?.birthday yields undefined instead of crashing on null.
  • ?. chains: customer?.birthday?.getFullYear().
  • ?.[] optional element access for possibly-null arrays.
  • Optional call: log?.('hi') runs only if log references a function.

Manual if (customer !== null && customer !== undefined) checks work but ?. is the idiomatic form.

Next: 002 — Introduction & Setup

Modules 00/01 covered: superset premise, static typing, setup (Node.js, global tsc, VS Code), first program with ES5 emit, tsconfig essentials, sourceMap debugging, inference, any avoidance, arrays, tuples with push caveat, const enum preference, function arity and params, objects, aliases, unions, intersections, narrowing, strict null checks with ?. chaining. Frame 22 (1:02:59) is the one code screenshot (literal types); frames 1-21 are talking-head shots and remaining code is reconstructed from audio. Transcript notes: Mosh Hamedani, codewithmosh.com, code.visualstudio.com, superset, tsconfig.json, dist/, let, tuple, type checking, catch these bugs, draggable/resizable, chaining operator.