Tutorial TypeScript JavaScript Developer Tools Cheat Sheet

Free TypeScript Types Cheat Sheet Online — Interactive Reference for Developers

· 16 min read

TypeScript has become the default language for serious frontend and fullstack development. Over 80% of new JavaScript projects now adopt TypeScript, and every major framework — React, Vue, Angular, Next.js, SvelteKit, NestJS — provides first-class TypeScript support. Yet the type system remains the steepest part of the learning curve. Developers who are comfortable with let x: string often freeze when confronted with generic constraints, mapped types, conditional type inference, or the subtle differences between interface and type.

Our free interactive TypeScript types cheat sheet maps sixty-five type concepts across nine territories of the type system. Each entry includes a concise explanation, a copyable code example, and color-coded category tags for instant visual scanning. The Cartographer's Type Atlas aesthetic — deep navy-black background, faint经纬 grid lines, drifting constellation particles, and Playfair Display cartographic headings — turns type exploration into navigation. Everything runs in your browser. No compilation server, no signup, no data collection.

Why TypeScript Types Deserve a Dedicated Cheat Sheet

TypeScript's type system is Turing complete. That statement is not hyperbole — you can write programs entirely inside type annotations that compile and execute at the type level. While most developers will never need that power, the same expressivity means that even everyday tasks involve choices: Should I use an interface or a type alias? When do I need a generic constraint? How do I extract the return type of a function? What is the difference between unknown and any?

A well-organized type reference provides three benefits. First, it reduces the cognitive load of memorizing syntax and behavior. Second, it surfaces type patterns you might not know exist. Many working developers have never used infer to extract nested types, built a mapped type with key remapping, or leveraged Template Literal Types to generate event names dynamically. Third, it accelerates code review and debugging by giving you a shared vocabulary for describing type constraints.

TypeScript evolves rapidly. New utility types like Awaited (4.5), satisfies operator (4.9), and decorator metadata (5.2) arrive regularly. A living cheat sheet keeps your mental model current without forcing you to read every release note.

Basic Types — The Foundation

Every TypeScript program rests on primitive types. string, number, and boolean are the obvious ones, but the type system also provides null, undefined, symbol, and bigint for completeness. Understanding the seven primitive types is essential before moving to composite structures.

string, number, boolean

These three types cover the vast majority of value types in most programs. TypeScript also supports literal types, where a specific value becomes the type itself.

let name: string = "Alice";
let count: number = 42;
let active: boolean = true;

// Literal types
let direction: "north" | "south" = "north";

null and undefined

TypeScript distinguishes between null (intentional absence) and undefined (uninitialized). With strictNullChecks enabled — which every project should enable — these types do not automatically widen to every other type. This catches an entire class of runtime errors at compile time.

let empty: null = null;
let notSet: undefined = undefined;

function greet(name: string | null) {
  if (name) console.log(`Hello ${name}`);
}

any and unknown

any opts out of type checking entirely. It is a trap door to JavaScript's dynamic behavior and should be avoided in production code. unknown is the type-safe alternative. It accepts any value but requires type narrowing before use, forcing you to validate assumptions explicitly.

let unsafe: any = fetchData();
unsafe.toFixed(); // Compiles, may crash at runtime

let safe: unknown = fetchData();
if (typeof safe === "number") {
  safe.toFixed(); // Guaranteed safe
}

never and void

void means a function returns nothing. never means a value never occurs — functions that always throw, infinite loops, or exhaustive type checks that eliminate all remaining possibilities. The never type is surprisingly powerful for ensuring that switch statements and conditional branches are complete.

function log(msg: string): void {
  console.log(msg);
}

function fail(msg: string): never {
  throw new Error(msg);
}

type Shape = { kind: "circle" } | { kind: "square" };
function area(s: Shape) {
  switch (s.kind) {
    case "circle": return Math.PI;
    case "square": return 1;
    default: return s; // s is never here if all cases are covered
  }
}

Interfaces — Defining Object Shapes

Interfaces are the primary tool for describing object structures in TypeScript. They support optional properties, readonly modifiers, index signatures, and inheritance through extension. Interfaces also participate in declaration merging, meaning multiple declarations of the same interface name are automatically combined.

Interface Basics

An interface defines a contract. Any object assigned to an interface type must satisfy all required properties with compatible types.

interface User {
  id: number;
  name: string;
  email: string;
}

const user: User = { id: 1, name: "Alice", email: "a@b.com" };

Optional and Readonly Properties

Mark properties optional with ? and immutable with readonly. These modifiers document intent and let the compiler enforce constraints.

interface Config {
  host: string;
  port?: number;
  readonly apiKey: string;
}

const cfg: Config = { host: "localhost", apiKey: "secret" };
// cfg.apiKey = "new"; // Error: readonly

Extending Interfaces

Interfaces inherit properties from other interfaces using extends. This creates clean hierarchies without repeating field definitions.

interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

const dog: Dog = { name: "Rex", breed: "Labrador" };

Type Aliases — Flexible Type Definitions

Type aliases create new names for types. Unlike interfaces, they can represent primitives, unions, intersections, tuples, and complex expressions. The type keyword is more general than interface, but it does not support declaration merging.

Union and Intersection Types

Union types (|) allow a value to be one of several types. Intersection types (&) require a value to satisfy all combined types simultaneously. Both are fundamental to expressive type modeling.

type Status = "pending" | "success" | "error";
type ID = string | number;

type A = { a: string };
type B = { b: number };
type C = A & B; // Must have both a and b

const c: C = { a: "hi", b: 1 };

Tuple Types

Tuples are fixed-length arrays with known types at each position. They are ideal for representing coordinate pairs, key-value entries, or function argument lists.

type Point = [number, number];
type Entry = [string, number, boolean];

const p: Point = [10, 20];
const entry: Entry = ["key", 42, true];

Interface vs Type — When to Choose Which

The choice between interface and type is one of the most common TypeScript questions. Interfaces are preferred for object shapes that might need extension, especially in public APIs, because they support declaration merging. Type aliases are preferred for unions, intersections, tuples, mapped types, and any type expression that is not a plain object shape. In practice, many teams use interfaces for data models and type aliases for utility transformations.

Unions and Intersections — Combining Types

Union types are the backbone of type-safe handling of heterogeneous data. The key challenge with unions is narrowing — refining a union to a specific member before accessing type-specific properties.

Type Narrowing with typeof and instanceof

TypeScript understands common JavaScript runtime checks and uses them to narrow types within control flow blocks.

function process(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase(); // string here
  }
  return value.toFixed(2); // number here
}

class Dog { bark() {} }
class Cat { meow() {} }

function speak(animal: Dog | Cat) {
  if (animal instanceof Dog) animal.bark();
  else animal.meow();
}

Discriminated Unions

The discriminated union pattern adds a shared literal property to every member of a union. This creates a type-safe tag that TypeScript can use for exhaustive narrowing.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number }
  | { kind: "triangle"; base: number; height: number };

function area(s: Shape) {
  switch (s.kind) {
    case "circle": return Math.PI * s.radius ** 2;
    case "square": return s.side ** 2;
    case "triangle": return 0.5 * s.base * s.height;
  }
}

Generics — Type Parameters

Generics enable functions, interfaces, and classes to operate on any type while preserving type information. They are the bridge between reusable code and type safety. Without generics, you would choose between duplicating code for every type or losing type information with any.

Generic Functions

A generic function captures the type of its argument and uses it in the return type.

function identity<T>(arg: T): T {
  return arg;
}

const num = identity<number>(42);
const str = identity("hello"); // inferred as string

Generic Constraints

Sometimes you need to restrict a generic to types that satisfy certain properties. The extends keyword on a type parameter creates this constraint.

interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): T {
  console.log(arg.length);
  return arg;
}

logLength("hello");
logLength([1, 2, 3]);

keyof and Lookup Types

The keyof operator extracts the keys of a type as a union of string literals. Combined with generics, it enables type-safe property access.

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Alice", age: 30 };
const n = getProperty(user, "name"); // string

Utility Types — TypeScript's Built-in Toolkit

TypeScript ships with a rich set of utility types that transform existing types. These are implemented in the standard library using mapped and conditional types, which means you can build your own versions if you understand the underlying mechanics.

Partial, Required, Readonly

These three utilities modify the optionality and mutability of object properties.

interface User {
  id: number;
  name: string;
  email: string;
}

type UserUpdate = Partial<User>;     // All optional
type StrictUser = Required<UserUpdate>; // All required
type FrozenUser = Readonly<User>;     // All readonly

Pick and Omit

Pick creates a subtype with only selected keys. Omit creates a subtype without specified keys. Both are indispensable for building public-facing DTOs from internal models.

type UserPreview = Pick<User, "id" | "name">;
type PublicUser = Omit<User, "password">;

Record, Exclude, Extract

Record builds an object type with uniform value types. Exclude and Extract operate on unions to remove or select specific members.

type PageInfo = Record<"home" | "about", { title: string }>;

type T = "a" | "b" | "c";
type WithoutA = Exclude<T, "a">; // "b" | "c"
type OnlyA = Extract<T, "a">;    // "a"

Parameters, ReturnType, InstanceType

These utilities introspect function and constructor types to extract their component types.

type Fn = (a: string, b: number) => boolean;
type Args = Parameters<Fn>;      // [string, number]
type Result = ReturnType<Fn>;    // boolean

class User { constructor(public name: string) {} }
type UserInstance = InstanceType<typeof User>; // User

Awaited

Awaited recursively unwraps Promise types to reveal the resolved value type. It handles nested promises gracefully, which manual extraction cannot.

type P = Promise<Promise<string>>;
type Result = Awaited<P>; // string

async function fetchUser(): Promise<{ id: number }> {
  return { id: 1 };
}
type UserData = Awaited<ReturnType<typeof fetchUser>>; // { id: number }

Mapped Types — Transforming Object Structures

Mapped types create new object types by transforming the properties of an existing type. They are the engine behind Partial, Readonly, and most custom utility types. The syntax uses in keyof to iterate over keys.

Basic Mapped Type

type Flags<T> = {
  [Property in keyof T]: boolean;
};

interface Feature {
  darkMode: string;
  beta: number;
}

type FeatureFlags = Flags<Feature>;
// { darkMode: boolean; beta: boolean }

Modifiers and Key Remapping

Mapped types can add or remove readonly and ? modifiers, and remap keys using the as clause introduced in TypeScript 4.1.

type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

type Optional<T> = {
  [P in keyof T]?: T[P];
};

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface Person {
  name: string;
  age: number;
}

type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }

Conditional Types — Type-Level Logic

Conditional types select one of two types based on a type relationship test. They are the foundation of advanced type manipulation and the mechanism behind Exclude, Extract, and ReturnType.

Basic Conditional Type

type IsString<T> = T extends string ? true : false;

type A = IsString<string>;  // true
type B = IsString<number>;  // false

infer Keyword

The infer keyword declares a type variable within a conditional type, allowing you to extract and name a type from a larger structure. It is the secret behind ReturnType, Parameters, and countless custom utilities.

type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function greet() { return "hello"; }
type G = MyReturnType<typeof greet>; // string

Distributive Conditionals

When a conditional type acts on a naked type parameter, it distributes over unions automatically. This is usually desired, but you can disable it by wrapping the parameter in a tuple.

type ToArray<T> = T extends any ? T[] : never;
type A = ToArray<string | number>; // string[] | number[]

// Non-distributive:
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type B = ToArrayNonDist<string | number>; // (string | number)[]

Advanced Patterns

Beyond the standard features, TypeScript supports several advanced patterns that solve specific architectural problems.

Type Guards

User-defined type guard functions narrow types using a predicate return type. They centralize validation logic and make control flow more readable.

function isString(value: unknown): value is string {
  return typeof value === "string";
}

function process(value: string | number) {
  if (isString(value)) {
    console.log(value.toUpperCase());
  }
}

Template Literal Types

Template literal types construct new string literal types from templates. Combined with mapped types, they generate entire APIs from string patterns.

type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"

type LocaleIDs = "welcome_email" | "email_heading";
type AllIDs = `${LocaleIDs}_id`; // "welcome_email_id" | "email_heading_id"

Branded Types

Branded types create nominal typing within TypeScript's structural type system. They prevent accidental mixing of semantically different values that happen to share the same underlying type.

type UserID = string & { __brand: "UserID" };
type OrderID = string & { __brand: "OrderID" };

function createUserID(id: string): UserID {
  return id as UserID;
}

const uid = createUserID("u-123");
// Cannot assign UserID to OrderID even though both are strings

Recursive Types

Recursive types reference themselves, enabling type-safe representations of nested and tree-like data structures.

type JSONValue =
  | string
  | number
  | boolean
  | null
  | JSONValue[]
  | { [key: string]: JSONValue };

const data: JSONValue = {
  name: "root",
  children: [{ name: "leaf" }]
};

Interactive TypeScript Types Cheat Sheet

Reading about types is useful, but exploring them interactively is faster. Our free TypeScript Types Cheat Sheet organizes every concept from this article into a searchable, filterable interface. Click any category tab to explore a territory of the type system. Use the search bar to find specific syntax instantly. Click the Copy button on any code block to grab the example and paste it into your editor.

The Cartographer's Type Atlas aesthetic makes the interface memorable. Deep navy-black background evokes a navigator's chart room. Faint经纬 grid lines suggest longitude and latitude. Drifting constellation particles mark key waypoints. Each category receives a distinct cartographic color — terrain brown for basic types, ocean blue for interfaces, forest green for type aliases, volcano red for unions, starlight purple for generics, desert orange for utility types, glacier cyan for mapped types, lightning yellow for conditional types, and mist gray for advanced patterns.

Related Developer Tools

TypeScript is just one piece of the modern developer toolkit. Explore these related references to round out your workflow:

Conclusion

TypeScript's type system is one of the most powerful static analysis tools available to frontend and fullstack developers. From simple primitive annotations to Turing-complete type-level programming, the system scales with your needs. The key is to build your knowledge incrementally — master basic types and interfaces first, then explore generics, utility types, mapped types, and conditional types as you encounter real problems that require them.

Keep the TypeScript Types Cheat Sheet bookmarked. The next time you forget the exact syntax for a generic constraint, need to choose between Pick and Omit, or want to explore whether infer can simplify a complex transformation, it will be one search away.

Found this useful? Check out our free developer tools or browse more articles.