BiTree
  • Search For Lessons
  • Curriculum
  • Pricing
  • For Educators
  • Become a Tutor
  • About
  • Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
BiTree

Live coding lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Search For Lessons
  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Become a Tutor
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 BiTree. All rights reserved.
Curriculum/Web Development/TypeScript Fundamentals
50 minBeginner

TypeScript Fundamentals

After this lesson, you will be able to: Write TypeScript that uses primitive types, arrays, objects, interfaces, type aliases, union types, and function signatures; configure a tsconfig.json with strict mode; and explain why TypeScript is the professional default for new web projects in 2025.

TypeScript is JavaScript with a type checker layered on top. Microsoft built it in 2012; it's now the default for new web codebases at Google, Meta, Microsoft, Stripe, Vercel, and almost every modern startup. Every serious React, Next.js, and Node.js codebase you'll see at work in 2025 is written in TypeScript. This lesson takes you from never having written a type annotation to fluent in the everyday TypeScript surface you'll meet in real code. You won't be a TypeScript expert in 50 minutes, but you will be able to read it, modify it, and start writing it without panic.

Prerequisites:JavaScript and the DOM

Why TypeScript exists

JavaScript has no type checking. `"5" * 2` is `10`. `[] + []` is `""`. `user.naem` (typo) is `undefined`, no error, until your page crashes in production. Every reasonably-sized JS codebase eventually accumulates dozens of these bugs. TypeScript catches them at the moment you type them. The compiler reads your code, infers what types each variable should be, and tells you when you're using a value the wrong way — before the code ever runs. Three concrete payoffs: (1) refactors become safe (rename a field, the compiler flags every reference that needs updating); (2) IDE autocomplete actually knows what you can call on a value; (3) function signatures double as documentation. The cost: a build step, slightly more verbose code, and a learning curve measured in days, not months.

Why it's the 2025 professional default

In 2025, the question is no longer "should I learn TypeScript?" It's table stakes. Stripe rewrote its frontend in TS. Microsoft built VS Code in it. Vercel ships Next.js docs telling you to use it. New React tutorials default to it. JetBrains and StackOverflow surveys consistently put TS in the top 5 most-loved languages. The hiring filter: junior developer job postings now routinely list TypeScript as required, not nice-to-have. A candidate who only writes JS is at an immediate disadvantage versus one who can sit down in a TS codebase on day one.

The first taste — types catch your bug at compile time

The function below adds two numbers. In JavaScript, calling it with a string would silently concatenate. In TypeScript, the compiler refuses to ship.

tsx
// JavaScript — silently wrong
function add(a, b) {
return a + b;
}
add(2, "3"); // "23" — probably not what you wanted
// TypeScript — caught at compile time
function addTs(a: number, b: number): number {
return a + b;
}
addTs(2, "3");
// Error: Argument of type 'string' is not assignable to parameter of type 'number'.
// The bug never makes it to the browser.

Setup — tsc, tsconfig.json, strict mode

Install TypeScript locally per project. The `tsconfig.json` controls how strict the checker is. For new code, always start with `strict: true` — anything less and you're paying for the build step without getting most of the value.

bash
# Install
npm install -D typescript
# Create tsconfig.json with sensible defaults
npx tsc --init
# Edit tsconfig.json — turn strict mode ON (default in newer scaffolds)
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"strict": true, // CRITICAL — enables noImplicitAny + strictNullChecks + more
"esModuleInterop": true,
"skipLibCheck": true,
"jsx": "preserve" // when using with React/Next.js
}
}
# Type-check (no emit — Next.js/Vite usually transpile separately)
npx tsc --noEmit

Primitive types and type inference

TypeScript can infer most types from context. Annotate function parameters and exported declarations; let inference handle the rest.

tsx
// Explicit annotations
const name: string = "Alex";
const age: number = 17;
const isStudent: boolean = true;
// Type inference — TypeScript knows these without you saying so
const greeting = "Hello"; // inferred as string
const count = 0; // inferred as number
const hobbies = ["code", "music"]; // inferred as string[]
// null and undefined are distinct types in TS
let maybeName: string | null = null;
maybeName = "Alex"; // OK
maybeName = 42; // Error: Type 'number' not assignable
// 'any' is the escape hatch — disables type checking for that value
let anything: any = 5;
anything = "now a string"; // no error, but no protection either
// Use 'unknown' instead when you genuinely don't know the shape yet:
let unknownVal: unknown = JSON.parse(input);
if (typeof unknownVal === "string") {
console.log(unknownVal.toUpperCase()); // narrowed to string, safe
}

Arrays, tuples, and objects

Real code is mostly arrays and objects. TS gives you precise ways to describe both.

tsx
// Arrays
const names: string[] = ["Alex", "Sam"];
const scores: Array<number> = [95, 88, 72]; // same as number[]
// Tuples — fixed-length, position-typed arrays
const coord: [number, number] = [40.7, -74.0];
const pair: [string, number] = ["age", 17];
// Object types — inline
function printUser(user: { name: string; age: number }) {
console.log(`${user.name} is ${user.age}`);
}
printUser({ name: "Alex", age: 17 });
// Optional properties
function render(user: { name: string; nickname?: string }) {
return user.nickname ?? user.name;
}

Interfaces and type aliases

When the same object shape appears in multiple places, give it a name. `interface` and `type` both work for object shapes; `type` is more flexible (unions, primitives), `interface` extends more naturally.

tsx
// Interface — most common for object shapes
interface User {
id: string;
name: string;
email: string;
isAdmin?: boolean; // optional
}
function sendEmail(user: User, subject: string) {
console.log(`To ${user.email}: ${subject}`);
}
// Type alias — works for objects too, AND for unions/primitives
type Status = "PENDING" | "CONFIRMED" | "CANCELLED";
function setStatus(orderId: string, status: Status) {
// status is constrained to those three literal strings
}
setStatus("o-1", "CONFIRMED");
setStatus("o-1", "WAITING"); // Error: not assignable to type Status
// Extending interfaces
interface AdminUser extends User {
permissions: string[];
}

Functions — typing parameters, returns, and optional args

Annotate parameters always; annotate return types when the function is exported or when you want to enforce a contract explicitly.

tsx
// Explicit parameter and return types
function add(a: number, b: number): number {
return a + b;
}
// Optional + default parameters
function greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}`;
}
// Arrow function with types
const multiply = (a: number, b: number): number => a * b;
// Function type as a parameter
function repeat(fn: (x: number) => number, times: number) {
let value = 1;
for (let i = 0; i < times; i++) value = fn(value);
return value;
}
// async functions return Promise<T>
async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return await response.json() as User;
}

💡 Strict mode is non-negotiable for new code

Always set `"strict": true` in `tsconfig.json` for any new project. Strict mode bundles `noImplicitAny`, `strictNullChecks`, and several other guards that prevent the most common TS-without-protection bugs. Codebases that turn strict off (or never turned it on) get all of TS's cost with little of its value — and adopting it later is painful because you have to fix every error at once. Start strict; stay strict.

Add types to a JavaScript snippet

The starter is a small JavaScript module. Convert it to TypeScript: annotate every parameter, give the `User` shape an interface, narrow `status` to a string-literal union, and have `formatUser` declare its return type. Required patterns check for the interface, the literal union, and a return-type annotation.

Loading exercise…
Quick Check

When should you reach for `any` in TypeScript?

Pick the most accurate answer for a 2025 codebase.

💡 Common mistakes only experienced devs catch

Five TypeScript traps that even good juniors fall into. (1) Reaching for `any` to silence an error — you've just disabled the tool you're paying for. Use `unknown` and narrow, or fix the underlying type. (2) Skipping `strict: true` to 'get started faster' — you end up with the build cost and none of the protection. Strict from day one is far easier than enabling it later. (3) Using `as` to lie to the compiler — `data as User` doesn't actually check the shape, it just tells TS to trust you. Validate with a runtime check (or a library like Zod) before asserting. (4) Annotating things TypeScript can already infer — `const name: string = "Alex"` is noise; `const name = "Alex"` is enough. Annotate function signatures and exported types; let inference handle the rest. (5) Confusing `interface` and `type` — for object shapes, either works; pick one and stick to it. `type` is required for unions, intersections, and primitive aliases. `interface` is required when you want declaration merging (rare).

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Working With APIs: HTTP, Status Codes, Postman, and fetch
Back to Web Development
Your First Full Website Project→