Skip to main content
Programming Language5.3+

TypeScript Essential Cheat Sheet

Comprehensive reference guide for TypeScript including type system, interfaces, generics, decorators, and professional development best practices.

Quick Reference

Most commonly used TypeScript features at a glance

FeatureJavaScriptTypeScript
Type annotationfunction add(a, b) {}function add(a: number, b: number): number {}
InterfaceN/Ainterface User { name: string; }
Type aliasN/Atype ID = string | number;
Union typeN/Alet value: string | number;
GenericsN/Afunction identity<T>(x: T): T {}
EnumN/Aenum Status { Active, Inactive }
ReadonlyN/Areadonly property: string;
IntersectionN/Atype Combined = A & B;
NullableN/Alet value: string | null;
Optional chaining?.validvalue?.property?.nested (typed)
About TypeScript
TypeScript adds static typing to JavaScript, catching errors at compile-time rather than runtime. It's fully compatible with JavaScript and compiles to clean, readable JS that runs everywhere.

Basic Types

Primitive type annotations

Primitive Types
Core TypeScript types
// String
const name: string = 'John';
const greeting: string = `Hello, ${name}!`;

// Number
const age: number = 30;
const price: number = 19.99;
const hex: number = 0xFF;

// Boolean
const active: boolean = true;
const isAdmin: boolean = false;

// Any (avoid when possible)
let anything: any = 42;
anything = 'string'; // No error

// Unknown (type-safe alternative)
let unknown: unknown = 42;
if (typeof unknown === 'number') {
  console.log(unknown + 1); // OK
}

// Void (no return value)
function log(): void {
  console.log('Message');
}

// Never (unreachable code)
function throwError(msg: string): never {
  throw new Error(msg);
}

// Symbol
const sym: symbol = Symbol('id');

// Null and Undefined
const nothing: null = null;
const undef: undefined = undefined;
Best Practice
Avoid any at all costs - use unknown instead. Use strict type checking to catch errors early.

Type Aliases & Unions

Create custom types

Type Aliases
Define reusable types
// Simple type alias
type ID = string | number;
const userId: ID = '12345';

// String literal type
type Status = 'active' | 'inactive' | 'pending';
const userStatus: Status = 'active';

// Union types
type Result = string | number | boolean;
let result: Result = true;

// Union with type guard
function process(value: string | number): string {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  return (value * 2).toString();
}

// Template literal types
type EndPoints = `/api/${string}`;
const endpoint: EndPoints = '/api/users';

// Discriminated unions
type Response = 
  | { status: 'success'; data: any }
  | { status: 'error'; error: string }
  | { status: 'loading' };

const handleResponse = (res: Response) => {
  if (res.status === 'success') {
    console.log(res.data);
  } else if (res.status === 'error') {
    console.log(res.error);
  }
};

Interfaces

Define object shapes and contracts

Interface Basics
Define object structures
// Basic interface
interface User {
  id: number;
  name: string;
  email?: string; // Optional property
  active: boolean;
}

// Use interface
const user: User = {
  id: 1,
  name: 'John',
  active: true
};

// Optional and readonly
interface Config {
  readonly apiUrl: string;
  timeout?: number;
  retries: number;
}

// Method signature
interface Logger {
  log(message: string): void;
  error(message: string): Error;
}

// Index signature (dynamic keys)
interface Dictionary {
  [key: string]: string;
}

const dict: Dictionary = {
  hello: 'world',
  foo: 'bar'
};

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

// Multiple inheritance
interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface Document extends User, Timestamped {
  content: string;
}
Advanced Interfaces
Complex interface patterns
// Generic interface
interface Repository<T> {
  getAll(): Promise<T[]>;
  getById(id: number): Promise<T>;
  create(item: T): Promise<T>;
  update(id: number, item: T): Promise<T>;
  delete(id: number): Promise<void>;
}

// Callable interface
interface CallableFunc {
  (x: number): number;
  property: string;
}

// Constructor signature
interface Constructor<T> {
  new (...args: any[]): T;
}

// Hybrid type
interface API {
  (config: string): void;
  baseUrl: string;
  version: string;
}

// Conditional types with interfaces
interface Response<T> {
  data: T;
  status: T extends null ? 'empty' : 'full';
}

// Function overloading in interface
interface Converter {
  convert(value: string): number;
  convert(value: number): string;
  convert(value: Date): string;
}

// Mapped types
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};
Interface vs Type
Use interface for object shapes and contracts. Use typefor unions, tuples, and primitive aliases.

Classes

Object-oriented programming with TypeScript

Class Declaration & Modifiers
Define and use classes with type safety
// Basic class
class User {
  // Property with type
  id: number;
  name: string;
  email: string;
  
  // Constructor
  constructor(id: number, name: string, email: string) {
    this.id = id;
    this.name = name;
    this.email = email;
  }
  
  // Method
  greet(): string {
    return `Hello, ${this.name}!`;
  }
}

// Parameter properties (shorthand)
class Profile {
  constructor(
    public id: number,
    public name: string,
    private email: string,
    readonly createdAt: Date
  ) {}
}

// Visibility modifiers
class Config {
  public setting: string = 'default'; // Accessible everywhere
  protected secret: string = 'hidden'; // In class and subclasses
  private apiKey: string = 'secret'; // Only in this class
  readonly version: string = '1.0'; // Can't be changed
}

// Abstract class
abstract class Animal {
  abstract makeSound(): void;
  
  move(): void {
    console.log('Moving...');
  }
}

class Dog extends Animal {
  makeSound(): void {
    console.log('Woof!');
  }
}

// Getters and setters
class Account {
  private _balance: number = 0;
  
  get balance(): number {
    return this._balance;
  }
  
  set balance(amount: number) {
    if (amount >= 0) {
      this._balance = amount;
    }
  }
}

// Static members
class Math {
  static PI = 3.14159;
  
  static add(a: number, b: number): number {
    return a + b;
  }
}

console.log(Math.PI);
console.log(Math.add(5, 3));
Best Practice
Use parameter properties (public/private/protected in constructor) to reduce boilerplate. Mark unchanging properties as readonly.

Generics

Reusable type-safe code

Generic Functions & Classes
Create flexible, type-safe components
// Generic function
function identity<T>(value: T): T {
  return value;
}

const strResult = identity<string>('hello');
const numResult = identity<number>(42);

// Generic with constraint
function getLength<T extends { length: number }>(
  value: T
): number {
  return value.length;
}

getLength('hello'); // 5
getLength([1, 2, 3]); // 3

// Generic class
class Box<T> {
  constructor(private content: T) {}
  
  getContent(): T {
    return this.content;
  }
}

const stringBox = new Box<string>('hello');
const numberBox = new Box<number>(42);

// Multiple type parameters
function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}

// Default generic type
function createArray<T = string>(length: number): T[] {
  return new Array(length);
}

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

const user = { name: 'John', age: 30 };
const name = getProperty(user, 'name'); // string

Advanced Generics

Complex generic patterns

Advanced Generic Techniques
Powerful type manipulation
// Generic constraints
type StringOrNumber = string | number;

function process<T extends StringOrNumber>(
  value: T
): T {
  return value;
}

// Conditional types
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<42>; // false

// Extract from union
type Flatten<T> = T extends Array<infer U> ? U : T;
type Str = Flatten<string[]>; // string

// Generic async
async function fetchData<T>(url: string): Promise<T> {
  const response = await fetch(url);
  return response.json();
}

const users = await fetchData<User[]>('/api/users');

// Generic with indexed access
type GetReadonly<T> = {
  readonly [K in keyof T]: T[K];
};

// Partial and Required
type Optional<T> = {
  [K in keyof T]?: T[K];
};

type Mandatory<T> = {
  [K in keyof T]-?: T[K];
};

// Pick and Omit
type UserPreview = Pick<User, 'name' | 'email'>;
type UserWithoutPassword = Omit<User, 'password'>;

// Record type
type Roles = 'admin' | 'user' | 'guest';
const permissions: Record<Roles, string[]> = {
  admin: ['read', 'write', 'delete'],
  user: ['read', 'write'],
  guest: ['read']
};

Intersection & Utility Types

Combine and manipulate types

Intersection Types & Built-in Utilities
Advanced type composition
// Intersection type (&)
interface Named {
  name: string;
}

interface Aged {
  age: number;
}

type Person = Named & Aged;
const person: Person = {
  name: 'John',
  age: 30
};

// Utility types - Partial
interface User {
  id: number;
  name: string;
  email: string;
}

type PartialUser = Partial<User>;
const update: PartialUser = { name: 'Jane' };

// Readonly
type ReadonlyUser = Readonly<User>;
const frozen: ReadonlyUser = {
  id: 1,
  name: 'John',
  email: 'john@example.com'
};
// frozen.name = 'Jane'; // Error!

// Pick - select properties
type UserPreview = Pick<User, 'id' | 'name'>;

// Omit - exclude properties
type UserWithoutEmail = Omit<User, 'email'>;

// Record - create object with specific keys
type Status = 'active' | 'inactive';
const statusConfig: Record<Status, string> = {
  active: 'User is active',
  inactive: 'User is inactive'
};

// Exclude - remove from union
type NonString = Exclude<string | number | boolean, string>;
// NonString = number | boolean

// Extract - select from union
type StringOrNumber = Extract<string | number | boolean, string | number>;
// StringOrNumber = string | number

// NonNullable - remove null/undefined
type Nullable<T> = T | null | undefined;
type NonNull<T> = NonNullable<Nullable<T>>;

// ReturnType - get return type
type MyFunc = () => string;
type FuncReturn = ReturnType<MyFunc>; // string

// InstanceType - get instance type
type MyClass = new () => User;
type UserInstance = InstanceType<MyClass>; // User
Utility Types
Master Partial, Pick, Omit, and Record - they are fundamental for managing complex type hierarchies.

Decorators

Metadata and function enhancement

Decorator Syntax
Add metadata to classes and methods
// Enable decorators in tsconfig.json
// "experimentalDecorators": true

// Class decorator
function Serializable(constructor: Function) {
  constructor.prototype.toJSON = function() {
    return JSON.stringify(this);
  };
}

@Serializable
class User {
  name: string;
  age: number;
  
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

// Method decorator
function Deprecated(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  const oldMethod = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.warn(`Method ${propertyKey} is deprecated`);
    return oldMethod.apply(this, args);
  };
  return descriptor;
}

class API {
  @Deprecated
  oldMethod() {
    return 'Old implementation';
  }
}

// Property decorator
function Validate(target: any, propertyKey: string) {
  let value: string;
  
  const getter = () => value;
  const setter = (newValue: string) => {
    if (newValue.length < 3) {
      throw new Error('Too short');
    }
    value = newValue;
  };
  
  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter
  });
}

class Account {
  @Validate
  username: string = '';
}

Namespaces & Modules

Organize code effectively

Module System
Import/export with types
// Export types
export interface User {
  id: number;
  name: string;
}

export type UserID = number;

export class UserService {
  getUser(id: UserID): User {
    // Implementation
    return { id, name: 'John' };
  }
}

// Default export
export default UserService;

// Import variations
import UserService, { User, UserID } from './user';

// Import entire module
import * as UserModule from './user';

// Type-only import (avoids circular deps)
import type { User } from './user';

// Re-export
export { User, UserID } from './user';
export * as UserTypes from './user';

// Namespace (rarely used now)
namespace Geometry {
  export interface Point {
    x: number;
    y: number;
  }
  
  export function distance(a: Point, b: Point) {
    return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
  }
}

const point1: Geometry.Point = { x: 0, y: 0 };

Advanced Type Features

Powerful type manipulation techniques

Conditional & Mapped Types
Dynamic type generation
// Conditional type
type IsString<T> = T extends string 
  ? true 
  : false;

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

// Nested conditional
type Flatten<T> = 
  T extends Array<infer U> 
    ? U 
    : T extends object 
      ? T 
      : never;

type Test = Flatten<string[]>; // string

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

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

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

// As const (literal types)
const config = {
  API_URL: 'https://api.example.com',
  TIMEOUT: 5000
} as const;

type ConfigKey = keyof typeof config;
// 'API_URL' | 'TIMEOUT'
Template Literal Types
Create types from string patterns
// Template literal types
type EndPoint = `/api/${string}`;
const endpoint: EndPoint = '/api/users';

// Union combinations
type Method = 'get' | 'post' | 'put' | 'delete';
type Route = `${Method} /${string}`;
const route: Route = 'get /users';

// Type inference
function useAPI<T extends string>(
  method: Method,
  endpoint: T
): `${Method} ${T}` {
  return `${method} ${endpoint}`;
}

// Labeled tuple types
type Response = [
  status: number,
  data: string,
  error?: Error
];

const response: Response = [200, 'Success'];

// Readonly tuple
type ReadonlyTuple = readonly [
  readonly string[],
  readonly number[]
];

// Variadic tuple types
type Concat<T extends readonly unknown[]> = 
  readonly [...T, T];

type Result = Concat<[string, number]>;
// readonly [string, number, string, number]

Type Guards & Type Narrowing

Refine types safely

Type Guards & Narrowing Techniques
Safely work with union types
// typeof guard
function process(value: string | number): string {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  return (value * 2).toString();
}

// instanceof guard
class User {}
class Admin extends User {}

function handleUser(user: User | Admin) {
  if (user instanceof Admin) {
    user.permissions; // Admin properties
  }
}

// Custom type guard (type predicate)
interface Car {
  wheels: 4;
  drive(): void;
}

interface Bike {
  wheels: 2;
  pedal(): void;
}

function isCar(vehicle: Car | Bike): vehicle is Car {
  return (vehicle as Car).drive !== undefined;
}

const vehicle: Car | Bike = getCar();
if (isCar(vehicle)) {
  vehicle.drive(); // TypeScript knows it's a Car
}

// Property checking
type Admin = { role: 'admin'; deleteUser(): void };
type User = { role: 'user' };

function handleRole(user: Admin | User) {
  if ('deleteUser' in user) {
    user.deleteUser();
  }
}

// Discriminated union
type Result = 
  | { status: 'success'; data: any }
  | { status: 'error'; error: string };

function handle(result: Result) {
  switch (result.status) {
    case 'success':
      console.log(result.data);
      break;
    case 'error':
      console.log(result.error);
      break;
  }
}

// is operator (type assertion guard)
const value: string | number = '42';
if (typeof value === 'string' && value.includes('4')) {
  console.log('Contains 4');
}
Type Assertions
Use as sparingly. Prefer type guards for safety.as const is an exception - it's safe and useful.

Function Signatures & Overloading

Advanced function typing

Function Overloading & Signatures
Define multiple function signatures
// Function signature (no implementation)
declare function greet(name: string): string;

// Full function
function greet(name: string): string {
  return `Hello, ${name}!`;
}

// Overloading (multiple signatures)
function format(date: Date): string;
function format(date: Date, format: string): string;
function format(date: Date, format?: string): string {
  if (format) {
    return date.toString(); // Use format
  }
  return date.toString();
}

format(new Date()); // Works
format(new Date(), 'yyyy-MM-dd'); // Works

// Function with this context
interface Button {
  text: string;
  onClick(this: Button, e: Event): void;
}

// Generic function signature
interface Observer<T> {
  onNext(value: T): void;
  onError(error: Error): void;
  onComplete(): void;
}

// Rest parameters with types
function sum(...numbers: number[]): number {
  return numbers.reduce((a, b) => a + b, 0);
}

// Variadic tuple parameters
type StringNumberBooleans = [string, number, ...boolean[]];
type StringBooleansNumber = [string, ...boolean[], number];

function f1(...args: StringNumberBooleans) {
  // args: [string, number, ...boolean[]]
}

// Function type expression
type Fn = (a: string) => number;
const fn: Fn = (str) => str.length;

// Construct signature
type Constructor<T> = {
  new(...args: any[]): T;
};

Modern Best Practices

Write professional TypeScript code

PracticeDescriptionExample
Strict modeEnable strict type checking"strict": true in tsconfig.json
Avoid anyUse unknown or proper typesconst value: unknown = 42;
Use readonlyPrevent accidental mutationsreadonly property: string;
Type inferenceLet TypeScript infer typesconst name = 'John'; // string
Generic constraintsLimit generic parametersT extends { length: number }
Discriminated unionsSafe union type handling{ status: 'success' | 'error' }
Type guardsNarrow types safelyif (typeof x === 'string')
Extract typesReuse types from valuestype User = typeof userObj;
Namespace typesOrganize related typesnamespace API { export type ... }
Utility typesUse built-in helpersPartial<T>, Pick<T, K>, Record<K, V>
Interface over typePrefer for object shapesinterface User { }
Strict null checksHandle null/undefinedstrictNullChecks: true

Configuration Best Practices:

// tsconfig.json - Recommended strict settings
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

Type Organization:

// Keep types organized
// types/index.ts
export type ID = string | number;
export type Status = 'active' | 'inactive';

export interface User {
  id: ID;
  name: string;
  status: Status;
}

// services/user.ts
import type { User, ID } from '../types';

export class UserService {
  async getUser(id: ID): Promise<User> {
    // Implementation
  }
}

// Use type-only imports to reduce bundle
import type { User } from '../types';

Performance & Optimization

Write efficient TypeScript

IssueProblemSolution
Large unionsType checking is slowUse discriminated unions instead
Deep nestingComplex type hierarchySimplify with utility types
Any usageLoses type informationUse proper types or unknown
Circular depsCompilation issuesUse type-only imports
Bundle sizeExtra code compiledUse tree-shaking, split code
ReflectionRuntime overheadDesign without heavy reflection
DecoratorsAdds metadata overheadUse only when needed
Strict modeMore checks = slowerBalance with actual needs
Incremental compileFull rebuild is slowEnable incremental in tsconfig
Type inferenceComplex inference slows buildExplicit types for large objects
Optimization Tools
Use tsc --diagnostics to profile compilation. Use --incrementalfor faster rebuilds during development.

Common Pitfalls & Solutions

Avoid these TypeScript gotchas

PitfallProblemSolution
any everywhereDefeats type safetyUse strict mode, proper types
Type assertion abuseHiding real issuesUse type guards instead
Circular importsCan't resolve typesReorganize, use type-only imports
Over-typingBoilerplate-heavyUse inference, stick to interfaces
Interface mutationSurprising behaviorUse readonly for immutability
Generic confusionOver-constrained typesSimplify, use extends properly
Strict null issuesEverything is nullableHandle null explicitly
Return type mismatchForgot return in functionExplicit return types help
Property orderingObject doesn't match typeUse Partial or Omit
Index signature abuseLoses type safetyUse Record or specific keys

TypeScript with Popular Frameworks

Framework-specific patterns

React with TypeScript
React component typing
// Functional component
interface Props {
  title: string;
  count?: number;
  onCount: (count: number) => void;
}

const Counter: React.FC<Props> = ({ 
  title, 
  count = 0, 
  onCount 
}) => {
  return <div>{title}: {count}</div>;
};

// With useState
import { useState } from 'react';

const [count, setCount] = useState<number>(0);
const [name, setName] = useState('');

// With useCallback
const handleClick = useCallback((e: React.MouseEvent) => {
  setCount(c => c + 1);
}, []);

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

// Custom hook
function useCustomHook(initial: string): [
  string, 
  (val: string) => void
] {
  const [state, setState] = useState(initial);
  return [state, setState];
}
Node.js Express Server
Express server with types
import express, { Request, Response } from 'express';

const app = express();

// Typed request/response
app.get('/users/:id', (req: Request<
  { id: string },
  any,
  any,
  any
>, res: Response) => {
  const userId = parseInt(req.params.id);
  // Handle request
});

// Typed request body
interface CreateUserBody {
  name: string;
  email: string;
}

app.post('/users', (req: Request<
  never,
  any,
  CreateUserBody
>, res: Response) => {
  const { name, email } = req.body;
  // Create user
});

// Custom error handling
interface CustomError extends Error {
  statusCode?: number;
}

app.use((
  err: CustomError,
  req: Request,
  res: Response,
  next: Function
) => {
  res.status(err.statusCode || 500)
    .json({ error: err.message });
});

Migration Checklist

Converting JavaScript to TypeScript

Step-by-Step Migration:

// 1. Setup TypeScript
npm install --save-dev typescript
npx tsc --init

// 2. Rename files
// file.js → file.ts

// 3. Add basic types
// function greet(name) { }
// ↓
function greet(name: string): string {
  return `Hello, ${name}!`;
}

// 4. Add interface/types
interface User {
  id: number;
  name: string;
}

// 5. Update imports
import type { User } from './types';

// 6. Fix type errors
// Use type guards and assertions

// 7. Enable strict mode gradually
// "strict": false → true

// 8. Run linter
npx tsc --noEmit

Migration Benefits:

  • Catch errors at compile-time
  • Better IDE autocomplete
  • Self-documenting code via types
  • Refactoring with confidence
  • Easier team collaboration
  • Fewer runtime errors
  • Better maintainability
  • Compatible with JavaScript
Gradual Adoption
You can enable allowJs: true in tsconfig to mix JavaScript and TypeScript during migration.

Quick Debugging Tips

Debug TypeScript issues

Debugging Techniques
Tools and methods for troubleshooting
// Show inferred type
const x = 42;
type X = typeof x; // number

// Hover over variable in VS Code to see type
const user = { name: 'John', age: 30 };
// Shows: { name: string; age: number; }

// Use conditional to test types
type Test = string extends string ? true : false;
// Hover shows: true

// Check if type is assignable
const test: string = 123; // Error (red squiggly)

// Use keyof to debug object types
interface User {
  name: string;
  age: number;
}

type Keys = keyof User; // 'name' | 'age'

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

type Test = Getters<User>;
// Shows: {
//   getName: () => string;
//   getAge: () => number;
// }

// Print types in errors (trick)
type Print<T> = T extends never ? 'T is never' : T;

// Use satisfies operator (TS 4.9+)
const config = {
  apiUrl: 'https://example.com'
} satisfies Record<string, string>;

// Show type in error message
type ShowType<T> = T & { __type: T };

// Enable diagnostics
// tsc --diagnostics --listFilesOnly
Pro Debugging
Hover over variables in VS Code with TypeScript extension to see inferred types. Use strict mode to catch more errors earlier.
Additional Resources
For more TypeScript learning resources, visit our TypeScript Documentation and practice with our TypeScript Exercises.