beginner languages TypeScript 5.x · Updated April 2026

TypeScript Basics Cheatsheet

A quick reference and learning journey for TypeScript 5.x, covering core concepts, essential syntax, and practical patterns for type-safe JavaScript.

· 8 min read · AI-reviewed

Quick Overview

TypeScript is a superset of JavaScript that adds static types, enabling developers to catch errors early, improve code readability, and build more robust applications, especially at scale. It compiles down to plain JavaScript, making it compatible with any browser, host, or OS. You’d reach for TypeScript to enhance the maintainability and predictability of your JavaScript projects. The content here is based on TypeScript 5.x.

To install the latest stable version globally:

# Install TypeScript globally
npm install -g typescript

Getting Started

Let’s set up a minimal TypeScript project and compile your first .ts file.

  1. Initialize a Node.js project:

    # Create a new directory and navigate into it
    mkdir my-ts-app
    cd my-ts-app
    
    # Initialize a new npm project
    npm init -y
  2. Install TypeScript locally: It’s recommended to install TypeScript as a development dependency per project to ensure consistent versions across team members.

    # Install TypeScript as a dev dependency
    npm install -D typescript
  3. Create a TypeScript configuration file (tsconfig.json): This file specifies compiler options and root files.

    # Initialize tsconfig.json with default settings
    npx tsc --init

    This command creates a tsconfig.json file. For a basic setup, you might want to uncomment and adjust target and outDir.

    // tsconfig.json
    {
      "compilerOptions": {
        "target": "ES2022", // Compile to a modern JavaScript version
        "module": "Node16", // Specify module code generation
        "outDir": "./dist",  // Output directory for compiled JavaScript
        "strict": true,      // Enable all strict type-checking options (recommended)
        "esModuleInterop": true, // Enables `import d from 'cjs'`
        "forceConsistentCasingInFileNames": true // Disallow inconsistent casing
      },
      "include": ["src/**/*"], // Include all .ts files in the src directory
      "exclude": ["node_modules", "dist"] // Exclude these directories
    }
  4. Write your first TypeScript code: Create a src directory and an index.ts file inside it.

    // src/index.ts
    function greet(name: string) {
      console.log(`Hello, ${name.toUpperCase()}!`);
    }
    
    greet("TypeScript"); // Works
    // greet(123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.
  5. Compile and run:

    # Compile your TypeScript code
    npx tsc
    
    # Run the compiled JavaScript file
    node dist/index.js

    Output: Hello, TYPESCRIPT!

Core Concepts

TypeScript introduces several fundamental concepts to JavaScript for static type-checking and enhanced development experience.

ConceptDescription
Static TypingAssigning types to variables, function parameters, and return values before code execution. This allows the TypeScript compiler to check for type-related errors at compile time.
Type InferenceThe TypeScript compiler’s ability to automatically deduce the type of a variable or expression when an explicit type is not provided. It infers types based on the assigned value.
InterfacesDefine the shape of objects, ensuring they have specific properties and methods. Useful for enforcing contracts for data structures and classes.
Type AliasesCreate new names for any type, including primitives, union types, intersection types, and complex object shapes. More flexible than interfaces for non-object types.
ClassesTypeScript fully supports ES6 classes with enhancements like access modifiers (public, private, protected), abstract classes, and interfaces implementation.
EnumsA way to define a collection of named constants. Useful for representing a set of distinct values (e.g., days of the week, status codes). As of TS 5.0, all enums are union enums.
GenericsWrite reusable components that work with a variety of types, rather than a single one. This makes components more flexible and type-safe.
Type NarrowingThe process by which TypeScript refines a broader type to a more specific one within conditional blocks (e.g., if statements or switch cases). Essential for type guards.

Essential Syntax

Here are the most common TypeScript syntax elements you’ll encounter.

Basic Types

// Explicit type annotations
let awesomeName: string = "Z2H";
let awesomeAge: number = 30;
let isActiveUser: boolean = true;

// Type inference (TypeScript infers types based on initial assignment)
let greeting = "Hello"; // Type: string
let count = 100;      // Type: number

// The 'any' type: Opts out of type checking for a variable. Use sparingly.
let data: any = "Can be anything";
data = 123;
data = false;

// The 'unknown' type: A type-safe counterpart to 'any'. Requires type checking before use.
let safeData: unknown = "Could be anything";
// console.log(safeData.length); // Error: Object is of type 'unknown'.
if (typeof safeData === 'string') {
  console.log(safeData.length); // OK, safeData is narrowed to string
}

// The 'void' type: For functions that do not return a value.
function logMessage(message: string): void {
  console.log(message);
}

// The 'null' and 'undefined' types
let n: null = null;
let u: undefined = undefined;

// The 'never' type: For functions that never return (e.g., always throw an error or loop infinitely).
function throwError(message: string): never {
  throw new Error(message);
}

Arrays

// Array of numbers
let numbers: number[] = [1, 2, 3];
let alsoNumbers: Array<number> = [4, 5, 6];

// Array of strings
let names: string[] = ["Alice", "Bob", "Charlie"];

// Array of mixed types (using a union type)
let mixedArray: (string | number)[] = ["text", 123, "more text"];

Functions

// Function with typed parameters and return type
function add(a: number, b: number): number {
  return a + b;
}

// Optional parameter (suffix with ?)
function buildName(firstName: string, lastName?: string): string {
  if (lastName) {
    return firstName + " " + lastName;
  }
  return firstName;
}

// Default parameter
function createGreeting(name: string = "Guest"): string {
  return `Welcome, ${name}!`;
}

// Arrow functions with types
const multiply = (a: number, b: number): number => a * b;

// Function type expression
type MathOperation = (x: number, y: number) => number;
const divide: MathOperation = (a, b) => a / b;

Interfaces

Define the shape of objects.

// Define an interface for a User object
interface User {
  id: number;
  name: string;
  email?: string; // Optional property
  readonly createdAt: Date; // Readonly property
}

// Implement the interface
const currentUser: User = {
  id: 1,
  name: "Jane Doe",
  createdAt: new Date()
};

// Extending interfaces
interface Admin extends User {
  role: "admin";
  permissions: string[];
}

const adminUser: Admin = {
  id: 2,
  name: "Admin User",
  createdAt: new Date(),
  role: "admin",
  permissions: ["read", "write", "delete"]
};

// Interfaces can also describe function types (less common than type aliases for functions)
interface SearchFunc {
  (source: string, subString: string): boolean;
}
const mySearch: SearchFunc = (src, sub) => src.includes(sub);

Type Aliases

Create a new name for a type. Can represent primitives, unions, intersections, or object shapes.

// Alias for a primitive type
type ID = string;
let userId: ID = "abc-123";

// Alias for a union of primitive types
type Status = "pending" | "approved" | "rejected";
let orderStatus: Status = "approved";

// Alias for an object shape (similar to interface)
type Point = {
  x: number;
  y: number;
};
const origin: Point = { x: 0, y: 0 };

// Type aliases with intersections (combining types)
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge;

const john: Person = { name: "John", age: 30 };

Union & Intersection Types

Combine existing types.

// Union Type: A value can be one of several types.
function printId(id: number | string) {
  console.log(`Your ID is: ${id}`);
}
printId(101);
printId("202");

// Intersection Type: A value must have properties of all combined types.
type Draggable = {
  drag: () => void;
};
type Resizable = {
  resize: () => void;
};
type UIWidget = Draggable & Resizable; // Must have both drag and resize

const myWidget: UIWidget = {
  drag: () => console.log("Dragging..."),
  resize: () => console.log("Resizing...")
};

Literal Types

Specify exact values a variable can have.

// String literal types
let clickEvent: "click" | "doubleClick" = "click";

// Numeric literal types
let zero: 0 = 0;

// Boolean literal types
let trueLiteral: true = true;

Enums

A set of named constants.

// Numeric Enum
enum Direction {
  Up = 1,
  Down, // Automatically 2
  Left, // Automatically 3
  Right // Automatically 4
}
let currentDirection: Direction = Direction.Up;
console.log(currentDirection); // Output: 1

// String Enum (values must be explicitly set)
enum LogLevel {
  ERROR = "ERROR",
  WARN = "WARN",
  INFO = "INFO",
  DEBUG = "DEBUG"
}
function log(message: string, level: LogLevel) {
  console.log(`${level}: ${message}`);
}
log("Something happened!", LogLevel.INFO); // Output: INFO: Something happened!

Classes

class Animal {
  // Public property (default)
  public name: string;
  // Private property (only accessible within the class)
  private age: number;
  // Protected property (accessible within the class and its subclasses)
  protected species: string;

  constructor(name: string, age: number, species: string) {
    this.name = name;
    this.age = age;
    this.species = species;
  }

  // Public method
  makeSound(sound: string): void {
    console.log(`${this.name} (${this.species}) says ${sound}`);
  }

  // Private method (not typically used in basic examples, but possible)
  private getAgeInHumanYears(factor: number = 7): number {
    return this.age * factor;
  }

  get ageDescription(): string {
    return `${this.name} is ${this.age} years old (approx ${this.getAgeInHumanYears()} human years).`;
  }
}

class Dog extends Animal {
  breed: string;

  constructor(name: string, age: number, breed: string) {
    super(name, age, "Dog"); // Call parent constructor
    this.breed = breed;
  }

  // Overriding a method
  makeSound(sound: string = "Woof!"): void {
    console.log(`${this.name} the ${this.breed} (${this.species}) says ${sound}`);
  }

  // Accessing protected property from parent class
  describeSpecies(): string {
    return `This is a ${this.species}.`;
  }
}

const myDog = new Dog("Buddy", 5, "Golden Retriever");
myDog.makeSound(); // Output: Buddy the Golden Retriever (Dog) says Woof!
console.log(myDog.name); // Output: Buddy
// console.log(myDog.age); // Error: Property 'age' is private
console.log(myDog.ageDescription); // Output: Buddy is 5 years old (approx 35 human years).

Generics

Write flexible, reusable code that works with different types while maintaining type safety.

// Generic function: identity returns whatever type it receives
function identity<T>(arg: T): T {
  return arg;
}

let output1 = identity<string>("myString"); // Type of output1 is string
let output2 = identity<number>(100);     // Type of output2 is number

// Generic interface
interface GenericBox<T> {
  value: T;
}

let stringBox: GenericBox<string> = { value: "hello" };
let numberBox: GenericBox<number> = { value: 123 };

// Generic classes
class GenericPair<K, V> {
  constructor(public key: K, public value: V) {}
}

let pair1 = new GenericPair<string, number>("age", 30);
let pair2 = new GenericPair<boolean, string>(true, "active");

Common Patterns

Defining API Response Shapes

Interfaces are excellent for defining the expected structure of data, especially for API responses.

// api.ts
interface Post {
  id: number;
  title: string;
  body: string;
  userId: number;
}

interface UserProfile {
  id: number;
  name: string;
  email: string;
  posts: Post[]; // Nested interface usage
}

async function fetchUserProfile(userId: number): Promise<UserProfile> {
  // In a real app, this would be an actual API call
  const userResponse = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`).then(res => res.json());
  const postsResponse = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}/posts`).then(res => res.json());

  // Simulate combining data
  const userProfile: UserProfile = {
    id: userResponse.id,
    name: userResponse.name,
    email: userResponse.email,
    posts: postsResponse.map((post: any) => ({
      id: post.id,
      title: post.title,
      body: post.body,
      userId: post.userId
    }))
  };
  return userProfile;
}

// Usage
fetchUserProfile(1).then(profile => {
  console.log(`User: ${profile.name}, Email: ${profile.email}`);
  console.log(`Posts: ${profile.posts.length}`);
  console.log(`First post title: ${profile.posts[0].title}`);
});

Type Guards for Runtime Checks

Type guards allow you to narrow down types within conditional blocks, ensuring type safety at runtime.

// Type guard using 'typeof' for primitives
function processValue(value: string | number) {
  if (typeof value === "string") {
    // TypeScript knows 'value' is a string here
    console.log(`Processing string: ${value.toUpperCase()}`);
  } else {
    // TypeScript knows 'value' is a number here
    console.log(`Processing number: ${value * 2}`);
  }
}
processValue("hello");
processValue(10);

// Type guard using 'instanceof' for classes
class Car {
  drive() { console.log("Driving car..."); }
}
class Bicycle {
  pedal() { console.log("Pedaling bicycle..."); }
}

type Vehicle = Car | Bicycle;

function startVehicle(vehicle: Vehicle) {
  if (vehicle instanceof Car) {
    // 'vehicle' is narrowed to Car
    vehicle.drive();
  } else {
    // 'vehicle' is narrowed to Bicycle
    vehicle.pedal();
  }
}
startVehicle(new Car());
startVehicle(new Bicycle());

// User-defined type guards (type predicates)
interface Fish { swim(): void; }
interface Bird { fly(): void; }

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function move(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim();
  } else {
    pet.fly();
  }
}

// Example usage
const nemo: Fish = { swim: () => console.log("Nemo is swimming!") };
const tweety: Bird = { fly: () => console.log("Tweety is flying!") };

move(nemo);
move(tweety);

tsconfig.json for Project Configuration

The tsconfig.json file is crucial for telling the TypeScript compiler how to behave.

// A common production-ready tsconfig.json setup
{
  "compilerOptions": {
    "target": "ES2022",                  // Specify ECMAScript target version
    "module": "Node16",                 // Specify module code generation
    "lib": ["ES2022", "DOM"],           // Specify library files to be included in the compilation
    "outDir": "./dist",                 // Redirect output structure to the directory
    "rootDir": "./src",                 // Specify the root directory of input files
    "strict": true,                     // Enable all strict type-checking options (highly recommended)
    "esModuleInterop": true,            // Enables `import d from 'cjs'`
    "forceConsistentCasingInFileNames": true, // Disallow inconsistent casing in file names
    "skipLibCheck": true,               // Skip type checking of all declaration files (*.d.ts)
    "declaration": true,                // Generate .d.ts files for emitted JavaScript.
    "sourceMap": true                   // Generate source maps for debugging
  },
  "include": ["src/**/*.ts"],         // Include specific files or patterns
  "exclude": ["node_modules", "dist", "**/*.test.ts"] // Exclude specific files or patterns
}

Gotchas & Tips

  • any vs unknown: Always prefer unknown over any when you don’t know the type of a value. unknown forces you to perform runtime type checks before you can use the value, making your code safer. any completely bypasses type checking.
  • Enable strict: true: This option in tsconfig.json enables a suite of strict type-checking options (like noImplicitAny, strictNullChecks, strictFunctionTypes, etc.) that catch a vast number of common errors. It’s the recommended baseline for new projects.
  • Type Assertions (as keyword): Use as Type to tell TypeScript “I know better, trust me on this type.” Be cautious, as it disables type checking for that specific assertion and can lead to runtime errors if you’re wrong.
    let someValue: unknown = "this is a string";
    let strLength: number = (someValue as string).length; // Asserting 'someValue' as string
  • Declaration Merging: Interfaces, unlike type aliases, can be declared multiple times, and TypeScript will merge their definitions. This is often used when extending types from external libraries or for modular augmentation.
    // In library.d.ts
    interface MyConfig {
      optionA: string;
    }
    
    // In your app.ts
    interface MyConfig {
      optionB: number; // Merges with the above
    }
    
    const config: MyConfig = {
      optionA: "foo",
      optionB: 123
    };
  • Version Specificity: TypeScript evolves rapidly. Features like ECMAScript decorators and const type parameters were significant additions in TypeScript 5.0 and beyond. Always refer to the official documentation for features introduced in specific versions.

Next Steps


Source: z2h.fyi/cheatsheets/typescript-basics — Zero to Hero cheatsheets for developers.