TypeScript Cheatsheet

A comprehensive guide to TypeScript - JavaScript with static types for building robust and maintainable applications

Basic Types

Primitive Types

Core primitive types in TypeScript

// Boolean type
const isDone: boolean = false;

// Number type (integers and floats)
const decimal: number = 6;
const float: number = 6.5;
const hex: number = 0xf00d;
const binary: number = 0b1010;

// String type
const color: string = "blue";
const greeting: string = `Hello, ${name}`;

// Special Types
const notSure: any = 4; // Can be anything
const unknown: unknown = 4; // Type-safe any
const v: void = undefined; // absence of value
const n: null = null;
const u: undefined = undefined;

// Symbol (ES2015)
const sym: symbol = Symbol("key");

// BigInt (ES2020)
const bigInt: bigint = 100n;

Arrays and Tuples

Working with collections of values

// Arrays
const list: number[] = [1, 2, 3];
const altList: Array<number> = [1, 2, 3]; // Generic

// Read-only array
const readOnly: ReadonlyArray<number> = [1, 2, 3];

// Tuple - fixed length array with known types
const tuple: [string, number] = ["hello", 10]; 
tuple[0]; // string
tuple[1]; // number

// Tuple with optional element
const optTuple: [string, number?] = ["hello"];

// Named tuple elements (TS 4.0+)
const point: [x: number, y: number] = [10, 20];
console.log(`x: ${point[0]}, y: ${point[1]}`);

Object Types

Working with object structures

// Object type annotation
const user: { name: string; age: number } = { 
  name: "John", 
  age: 30 
};

// Optional properties
const config: { debug?: boolean; mode: string } = {
  mode: "production"
};

// Index signatures
const scores: { [key: string]: number } = {
  math: 95,
  science: 90
};

// Read-only properties
const point: { readonly x: number; readonly y: number } = { 
  x: 10, 
  y: 20 
};

Interfaces & Types

Type Aliases

Create named types for reuse

// Type alias
type Point = {
  x: number;
  y: number;
};

// Using the type
const p: Point = { x: 10, y: 20 };

// Union types
type ID = number | string;
const id: ID = "abc123"; // or 123

// Literal types
type Direction = "up" | "down" | "left" | "right";
const move: Direction = "up";

// Combining types
type Employee = {
  id: ID;
  name: string;
};

// Extending types
type Person = { name: string };
type User = Person & { email: string };

// Function type
type Callback = (data: string) => void;
const fn: Callback = (data) => console.log(data);

Interfaces

Defining contracts for objects

// Basic interface
interface User {
  name: string;
  age: number;
}

// Optional properties
interface Config {
  color?: string;
  width: number;
}

// Read-only properties
interface Point {
  readonly x: number;
  readonly y: number;
}

// Method signatures
interface Calculator {
  add(a: number, b: number): number;
  subtract(a: number, b: number): number;
}

// Extending interfaces
interface Person {
  name: string;
}

interface Employee extends Person {
  department: string;
}

// Implementing interfaces
class Student implements Person {
  name: string;
  
  constructor(name: string) {
    this.name = name;
  }
}

Type vs Interface

Understanding when to use each

// Both can define object shapes
interface UserInterface {
  name: string;
}

type UserType = {
  name: string;
};

// Only interfaces can be merged (declaration merging)
interface API {
  get(url: string): Promise<any>;
}

interface API {
  post(url: string, data: any): Promise<any>;
}

// Only types can use advanced unions/intersections
type Status = "pending" | "fulfilled" | "rejected";

type Shape = Circle | Square;

type UserWithRole = User & { role: string };

// Interfaces for public APIs, Types for complex structures
// or when you need unions/intersections

Functions

Function Signatures

Typing functions and their parameters

// Basic function with types
function add(a: number, b: number): number {
  return a + b;
}

// Arrow function
const multiply = (a: number, b: number): number => a * b;

// Optional parameters
function greet(name: string, greeting?: string): string {
  return `${greeting || 'Hello'}, ${name}`;
}

// Default parameters
function createElement(tag: string, content: string = ''): string {
  return `<${tag}>${content}</${tag}>`;
}

// Rest parameters
function sum(...numbers: number[]): number {
  return numbers.reduce((total, n) => total + n, 0);
}

// Function overloads
function process(x: number): number;
function process(x: string): string;
function process(x: number | string): number | string {
  if (typeof x === 'number') {
    return x * 2;
  } else {
    return x.repeat(2);
  }
}

Generics

Create reusable components with different types

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

// Calling with explicit type
const num = identity<number>(42);

// Letting TypeScript infer the type
const str = identity("hello");

// Generic interfaces
interface Box<T> {
  value: T;
}

const numberBox: Box<number> = { value: 42 };
const stringBox: Box<string> = { value: "hello" };

// Generic constraints
interface Lengthwise {
  length: number;
}

function getLength<T extends Lengthwise>(arg: T): number {
  return arg.length;
}

// Now works with strings, arrays, but not numbers
getLength("hello");     // 5
getLength([1, 2, 3]);   // 3
// getLength(42);       // Error!

// Generic classes
class Queue<T> {
  private data: T[] = [];
  
  push(item: T): void {
    this.data.push(item);
  }
  
  pop(): T | undefined {
    return this.data.shift();
  }
}

Advanced Types

Utility Types

Built-in type transformations

// Partial - makes all properties optional
interface User {
  id: number;
  name: string;
  email: string;
}

function updateUser(user: User, updates: Partial<User>): User {
  return { ...user, ...updates };
}

// Required - makes all properties required
type PartialConfig = { debug?: boolean; mode?: string };
const config: Required<PartialConfig> = { 
  debug: true, 
  mode: "test" 
};

// Pick - create a type with a subset of properties
type UserBasics = Pick<User, 'id' | 'name'>;

// Omit - create a type without certain properties
type UserWithoutEmail = Omit<User, 'email'>;

// Record - create a dictionary with specific key and value types
const countries: Record<string, string> = {
  US: "United States",
  CA: "Canada",
  MX: "Mexico"
};

// Exclude - remove types from a union
type Numbers = 1 | 2 | 3 | 4;
type EvenNumbers = Exclude<Numbers, 1 | 3>; // 2 | 4

// Extract - extract types from a union
type Colors = "red" | "green" | "blue" | "black" | "white";
type RGB = Extract<Colors, "red" | "green" | "blue">; // "red" | "green" | "blue"

// NonNullable - remove null and undefined
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>; // string

Type Guards

Narrow down types in conditionals

// typeof type guard
function process(value: string | number) {
  if (typeof value === "string") {
    // TypeScript knows value is a string here
    return value.toUpperCase();
  } else {
    // TypeScript knows value is a number here
    return value.toFixed(2);
  }
}

// instanceof type guard
class Dog {
  bark() { return "Woof!"; }
}

class Cat {
  meow() { return "Meow!"; }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    // TypeScript knows animal is Dog
    return animal.bark();
  } else {
    // TypeScript knows animal is Cat
    return animal.meow();
  }
}

// in operator type guard
interface Fish { swim: () => void; }
interface Bird { fly: () => void; }

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    // TypeScript knows animal is Fish
    return animal.swim();
  } else {
    // TypeScript knows animal is Bird
    return animal.fly();
  }
}

// User-defined type guards
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function feed(pet: Fish | Bird) {
  if (isFish(pet)) {
    // TypeScript knows pet is Fish
    pet.swim();
  } else {
    // TypeScript knows pet is Bird
    pet.fly();
  }
}

Mapped Types

Transform properties of existing types

// Read-only mapped type
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

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

const readonlyUser: Readonly<User> = {
  id: 1,
  name: "John"
};

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

const optionalUser: Optional<User> = {
  // All fields are optional now
  name: "John"
};

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

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

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

type UserGetters = Getters<User>;
// { getId: () => number, getName: () => string }

Conditional Types

Types that depend on type conditions

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

type A = IsString<"hello">; // true
type B = IsString<123>;     // false

// Extract return type of a function
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function fetchUser() { return { id: 1, name: "John" }; }
type User = ReturnType<typeof fetchUser>; // { id: number, name: string }

// Extract parameter types
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

function greet(name: string, age: number) { return `Hello, ${name}`; }
type GreetParams = Parameters<typeof greet>; // [string, number]

// Distributive conditional types
type ToArray<T> = T extends any ? T[] : never;
type StrNumArr = ToArray<string | number>; // string[] | number[]

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

Type Declarations

Declaration Files

Creating and using .d.ts files

// In types.d.ts
declare module "my-module" {
  export function add(a: number, b: number): number;
  export const version: string;
}

// Using the module
import { add, version } from "my-module";

// Global declarations (window.d.ts)
interface Window {
  analytics: {
    track(event: string, properties?: object): void;
    identify(userId: string): void;
  };
}

// Using globals
window.analytics.track("Page View");

// Augmenting existing modules
declare module "react" {
  interface ComponentProps<T> {
    testId?: string;
  }
}

Type Assertions

Telling the compiler about types it can't infer

// "as" syntax (preferred)
const element = document.getElementById("root") as HTMLElement;

// Angle bracket syntax (not allowed in JSX)
const element2 = <HTMLElement>document.getElementById("root");

// Non-null assertion
function getLength(str: string | null) {
  // "!" tells TypeScript that str is definitely not null
  return str!.length;
}

// Const assertions
const colors = ["red", "green", "blue"] as const;
// Type is readonly ["red", "green", "blue"]

// Double assertion (use with caution)
const canvas = document.getElementById("canvas") as unknown as HTMLCanvasElement;

TypeScript & React

React Component Types

Common patterns for React components

// Functional component with typed props
interface GreetingProps {
  name: string;
  age?: number;
}

const Greeting: React.FC<GreetingProps> = ({ name, age }) => {
  return <h1>Hello, {name}! {age && `You are ${age} years old.`}</h1>;
};

// Alternative function declaration (preferred in modern TS-React)
function Greeting({ name, age }: GreetingProps) {
  return <h1>Hello, {name}! {age && `You are ${age} years old.`}</h1>;
}

// Props with children
interface LayoutProps {
  title: string;
  children: React.ReactNode;
}

const Layout = ({ title, children }: LayoutProps) => (
  <div>
    <h1>{title}</h1>
    <main>{children}</main>
  </div>
);

// Generic components
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

Hooks with TypeScript

TypeScript patterns for React Hooks

// useState with type inference
const [user, setUser] = useState({ name: "John", age: 30 });

// useState with type annotation
const [user, setUser] = useState<User | null>(null);

// useRef with HTMLElement
const inputRef = useRef<HTMLInputElement>(null);

// useEffect with typed dependencies
useEffect(() => {
  console.log(user.name);
}, [user]);

// useReducer with explicit types
interface State {
  count: number;
}

type Action = 
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset'; payload: number };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: action.payload };
  }
}

const [state, dispatch] = useReducer(reducer, { count: 0 });

// useContext with types
const ThemeContext = createContext<'light' | 'dark'>('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Child />
    </ThemeContext.Provider>
  );
}

function Child() {
  const theme = useContext(ThemeContext);
  return <div>Current theme: {theme}</div>;
}

Configuration & Tools

tsconfig.json

Common TypeScript configuration options

{
  "compilerOptions": {
    // Target ECMAScript version
    "target": "es2020",
    
    // Module system
    "module": "esnext",
    
    // Emit declaration files (.d.ts)
    "declaration": true,
    
    // Strict type checking
    "strict": true,
    
    // Error on unused variables
    "noUnusedLocals": true,
    
    // Error on unused parameters
    "noUnusedParameters": true,
    
    // Allow JavaScript files
    "allowJs": true,
    
    // Path aliases
    "baseUrl": ".",
    "paths": {
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    },
    
    // React JSX handling
    "jsx": "react",
    
    // Interop with CommonJS modules
    "esModuleInterop": true,
    
    // Skip type checking node_modules
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Migrating to TypeScript

Tips for migrating JavaScript to TypeScript

// 1. Start with allowJs and checkJs
// tsconfig.json
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    // Other options...
  }
}

// 2. Add // @ts-check to JavaScript files
// @ts-check
function add(a, b) {
  return a + b;
}

// 3. Add JSDoc type annotations
/**
 * @param {number} a First number
 * @param {number} b Second number
 * @returns {number} Sum of a and b
 */
function add(a, b) {
  return a + b;
}

// 4. Convert files one by one to .ts
// 5. Enable strict mode gradually
// 6. Use type assertions for complex cases
const user = JSON.parse(data) as User;

// 7. Create declaration files for external libs
// for-external-lib.d.ts
declare module 'external-lib' {
  export function doSomething(): void;
}

Type Definitions from npm

Working with DefinitelyTyped packages

// For libraries with built-in types:
// Just install the library
npm install react

// For libraries without types:
// Install @types package
npm install lodash
npm install @types/lodash

// Check if types are available:
// https://www.npmjs.com/~types

// Create your own types:
// mylib.d.ts
declare module 'mylib' {
  export function helper(): void;
  export const version: string;
}