Master TypeScript with this comprehensive cheatsheet covering everything from basic types and interfaces to advanced generics, decorators, and utility types. Includes practical code examples, configuration setup, and best practices.
- Basic Types
- Type Annotations & Inference
- Interfaces & Type Aliases
- Functions
- Classes
- Generics
- Enums
- Union & Intersection Types
- Type Guards & Narrowing
- Utility Types
- Modules & Namespaces
- Decorators
- Advanced Types
- Type Manipulation
- Error Handling
- Configuration (tsconfig.json)
- Best Practices & Patterns
// Primitive Types
let isCompleted: boolean = false;
let count: number = 42;
let decimal: number = 3.14;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let name: string = "TypeScript";
let template: string = `Hello, ${name}`;
// Arrays
let list: number[] = [1, 2, 3];
let genericList: Array<number> = [1, 2, 3];
let tuple: [string, number] = ["hello", 10];
// Special Types
let notSure: any = 4;
let u: undefined = undefined;
let n: null = null;
let obj: object = { key: "value" };
let voidFunc: () => void = () => console.log("no return");
// Never
function error(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
// Unknown (safer than any)
let uncertain: unknown = 4;
if (typeof uncertain === "string") {
let strLength: number = uncertain.length; // OK
}// Type Annotations
let variable: string = "hello";
let array: number[] = [1, 2, 3];
let object: { name: string; age: number } = { name: "John", age: 30 };
// Type Inference (automatic)
let inferred = "hello"; // TypeScript infers string
let numbers = [1, 2, 3]; // TypeScript infers number[]
// Contextual Typing
window.onmousedown = (event) => {
console.log(event.button); // event inferred as MouseEvent
};
// Type Assertions
let someValue: unknown = "this is a string";
let strLength1: number = (someValue as string).length;
let strLength2: number = (<string>someValue).length;
// Const Assertions
const config = {
api: "https://api.example.com",
timeout: 3000,
} as const;
// Non-null Assertion
let maybeNull: string | null = "hello";
let definitely: string = maybeNull!;// Interface
interface User {
readonly id: number;
name: string;
email?: string; // Optional property
[key: string]: any; // Index signature
}
// Extending Interfaces
interface Admin extends User {
role: "admin" | "superadmin";
permissions: string[];
}
// Type Alias
type Point = {
x: number;
y: number;
};
type ID = string | number;
type Callback = (data: any) => void;
// Interface vs Type
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
type AnimalType = {
name: string;
};
type DogType = AnimalType & {
breed: string;
};
// Declaration Merging (only interfaces)
interface Box {
height: number;
}
interface Box {
width: number;
}
// Box now has both height and width
// Implementing interfaces
class Rectangle implements Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}// Function Types
let myFunc: (x: number, y: number) => number;
myFunc = (a, b) => a + b;
// Optional & Default Parameters
function buildName(first: string, last?: string): string {
return last ? `${first} ${last}` : first;
}
function multiply(a: number, b: number = 1): number {
return a * b;
}
// Rest Parameters
function sum(...numbers: number[]): number {
return numbers.reduce((acc, val) => acc + val, 0);
}
// Function Overloads
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
return a + b;
}
// This Parameter
interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () => Card;
}
// Arrow Functions (lexical this)
const greet = (name: string): string => `Hello, ${name}`;
// Call Signatures
type DescribableFunction = {
description: string;
(someArg: number): boolean;
};
function doSomething(fn: DescribableFunction) {
console.log(fn.description + " returned " + fn(6));
}
// Constructor Signatures
type SomeConstructor = {
new (s: string): object;
};// Basic Class
class Animal {
// Properties
private name: string;
protected age: number;
readonly species: string;
// Constructor
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// Methods
public speak(): void {
console.log(`${this.name} makes a sound`);
}
// Getter/Setter
get getName(): string {
return this.name;
}
set setName(value: string) {
if (value.length > 0) this.name = value;
}
// Static methods
static create(name: string): Animal {
return new Animal(name, 0);
}
}
// Inheritance
class Dog extends Animal {
private breed: string;
constructor(name: string, age: number, breed: string) {
super(name, age);
this.breed = breed;
}
// Method override
speak(): void {
console.log(`${this.getName} barks`);
}
}
// Abstract Classes
abstract class Shape {
abstract getArea(): number;
abstract color: string;
printArea(): void {
console.log(`Area: ${this.getArea()}`);
}
}
class Circle extends Shape {
color: string = "red";
constructor(private radius: number) {
super();
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
}
// Parameter Properties (shorthand)
class Person {
constructor(
public name: string,
private age: number,
protected email: string,
readonly id: number
) {}
}
// Implementing multiple interfaces
interface Printable {
print(): void;
}
interface Loggable {
log(): void;
}
class Document implements Printable, Loggable {
print(): void { /* ... */ }
log(): void { /* ... */ }
}// Generic Functions
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("hello");
let inferred = identity("hello"); // Type inference
// Generic Interfaces
interface GenericRepository<T> {
getById(id: number): T;
getAll(): T[];
create(item: T): T;
update(id: number, item: T): T;
}
// Generic Classes
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
// Generic Constraints
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length);
return arg;
}
// Using Type Parameters in Constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// Generic with Multiple Types
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
// Default Generic Types
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
// Conditional Types with Generics
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"// Numeric Enums
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
enum Status {
Active = 1,
Inactive, // 2
Pending, // 3
}
// String Enums
enum Colors {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
// Heterogeneous Enums (mixed)
enum Mixed {
No = 0,
Yes = "YES",
}
// Computed Members
enum FileAccess {
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
}
// Const Enums (removed at compile time)
const enum ConstDirection {
Up,
Down,
Left,
Right,
}
// Reverse Mapping (only numeric enums)
enum Enum {
A
}
let a = Enum.A; // 0
let name = Enum[0]; // "A"
// Using enums
let dir: Direction = Direction.Up;
let color: Colors = Colors.Red;
// Enum as function parameter
function move(direction: Direction): void {
switch (direction) {
case Direction.Up: /* ... */ break;
// ...
}
}// Union Types
type StringOrNumber = string | number;
let value: StringOrNumber = 42;
value = "hello";
function printId(id: number | string) {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id.toFixed(2));
}
}
// Discriminated Unions
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number }
| { kind: "triangle"; base: number; height: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "rectangle": return shape.width * shape.height;
case "triangle": return (shape.base * shape.height) / 2;
}
}
// Intersection Types
type Employee = {
name: string;
id: number;
};
type Manager = {
department: string;
reports: string[];
};
type ManagerEmployee = Employee & Manager;
let manager: ManagerEmployee = {
name: "John",
id: 123,
department: "Engineering",
reports: ["Alice", "Bob"],
};
// Combining Unions and Intersections
type A = { a: string };
type B = { b: number };
type C = { c: boolean };
type Union = A | B | C;
type Intersection = A & B & C;// typeof Type Guard
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
}
// instanceof Type Guard
class Bird {
fly(): void { /* ... */ }
}
class Fish {
swim(): void { /* ... */ }
}
function move(animal: Bird | Fish) {
if (animal instanceof Bird) {
animal.fly();
} else {
animal.swim();
}
}
// Custom Type Guards (type predicates)
function isFish(pet: Bird | Fish): pet is Fish {
return (pet as Fish).swim !== undefined;
}
// in Operator Narrowing
type Cat = { meow: () => void };
type Dog = { bark: () => void };
function speak(animal: Cat | Dog) {
if ("meow" in animal) {
animal.meow();
} else {
animal.bark();
}
}
// Equality Narrowing
function example(x: string | number, y: string | boolean) {
if (x === y) {
// x and y are both string
x.toUpperCase();
y.toLowerCase();
}
}
// never Type for Exhaustiveness Checking
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function handleShape(shape: Shape): string {
switch (shape.kind) {
case "circle": return "Circle";
case "rectangle": return "Rectangle";
case "triangle": return "Triangle";
default: return assertNever(shape);
}
}// Partial<T> - Makes all properties optional
interface Todo {
title: string;
description: string;
completed: boolean;
}
type PartialTodo = Partial<Todo>;
// Required<T> - Makes all properties required
type RequiredTodo = Required<PartialTodo>;
// Readonly<T> - Makes all properties readonly
type ReadonlyTodo = Readonly<Todo>;
// Pick<T, K> - Picks specific properties
type TodoPreview = Pick<Todo, "title" | "completed">;
// Omit<T, K> - Omits specific properties
type TodoInfo = Omit<Todo, "completed">;
// Record<K, T> - Creates type with keys K and values T
type PageInfo = Record<"home" | "about" | "contact", { title: string }>;
// Exclude<T, U> - Excludes types from union
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<string | number | (() => void), Function>; // string | number
// Extract<T, U> - Extracts types from union
type T2 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable<T> - Removes null and undefined
type T3 = NonNullable<string | number | undefined | null>; // string | number
// Parameters<T> - Gets parameters of function type
type T4 = Parameters<(a: string, b: number) => void>; // [string, number]
// ConstructorParameters<T> - Gets constructor parameters
type T5 = ConstructorParameters<ErrorConstructor>; // [message?: string]
// ReturnType<T> - Gets return type
type T6 = ReturnType<() => string>; // string
// InstanceType<T> - Gets instance type
class MyClass {
x = 0;
y = 0;
}
type T7 = InstanceType<typeof MyClass>; // MyClass
// ThisParameterType<T> - Extracts 'this' parameter
function toHex(this: Number) {
return this.toString(16);
}
type T8 = ThisParameterType<typeof toHex>; // Number
// OmitThisParameter<T> - Removes 'this' parameter
type T9 = OmitThisParameter<typeof toHex>; // () => string// ===== Modules (ES6 style) =====
// Exporting
// file: math.ts
export function add(a: number, b: number): number {
return a + b;
}
export const PI = 3.14159;
export interface Calculator {
add(x: number, y: number): number;
}
export default class BasicCalculator implements Calculator {
add(x: number, y: number): number {
return x + y;
}
}
// Importing
import BasicCalc, { add, PI, Calculator } from './math';
import * as Math from './math';
import { add as sum } from './math';
// Dynamic Imports
async function loadModule() {
const module = await import('./math');
module.add(5, 3);
}
// Re-exports
export { add } from './math';
export { add as sum } from './math';
export * from './math';
// ===== Namespaces =====
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}
export class LettersOnlyValidator implements StringValidator {
isValid(s: string): boolean {
return /^[A-Za-z]+$/.test(s);
}
}
}
// Using namespaces
let validator: Validation.StringValidator;
validator = new Validation.LettersOnlyValidator();
// Ambient Modules (for existing JS libraries)
declare module "some-library" {
export function doSomething(): void;
export const version: string;
}
// Module Augmentation
import { Observable } from "rxjs";
declare module "rxjs" {
interface Observable<T> {
customMethod(): void;
}
}// Enable decorators in tsconfig.json: "experimentalDecorators": true
// Class Decorator
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return `Hello, ${this.greeting}`;
}
}
// Decorator Factory
function logger(prefix: string) {
return function (target: any) {
console.log(`${prefix} - ${target.name}`);
};
}
@logger("INFO")
class MyClass {}
// Method Decorator
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args);
const result = original.apply(this, args);
console.log(`Result:`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
// Property Decorator
function validate(target: any, propertyKey: string) {
let value: string;
const getter = () => value;
const setter = (newVal: string) => {
if (newVal.length < 3) {
throw new Error(`Invalid ${propertyKey}`);
}
value = newVal;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
});
}
class User {
@validate
name: string;
constructor(name: string) {
this.name = name;
}
}
// Parameter Decorator
function required(target: any, propertyKey: string, parameterIndex: number) {
console.log(`Parameter at index ${parameterIndex} is required`);
}
class Service {
greet(@required name: string) {
return `Hello, ${name}`;
}
}
// Accessor Decorator
function configurable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() {
return this._x;
}
}// Conditional Types
type IsNumber<T> = T extends number ? true : false;
type Result1 = IsNumber<42>; // true
type Result2 = IsNumber<"42">; // false
// Infer Keyword
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type StringType = UnpackArray<string[]>; // string
type NumberType = UnpackArray<number>; // number
// Mapped Types
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Optional<T> = {
[P in keyof T]?: T[P];
};
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
// Template Literal Types
type World = "world";
type Greeting = `hello ${World}`; // "hello world"
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// Keyof Type Operator
type Point = { x: number; y: number };
type P = keyof Point; // "x" | "y"
type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish; // number
// Typeof Type Operator
let s = "hello";
let n: typeof s; // string
function f() {
return { x: 10, y: 3 };
}
type F = typeof f; // () => { x: number; y: number; }
// Indexed Access Types
type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"]; // number
type I1 = Person["age" | "name"]; // string | number
// Conditional Types with Infer
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type FuncReturnType = ReturnType<() => string>; // string
// Distributive Conditional Types
type ToArray<T> = T extends any ? T[] : never;
type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]// Creating Types from Values
const config = {
host: "localhost",
port: 3000,
ssl: true,
};
// typeof type operator
type Config = typeof config;
// Index types
type ConfigKey = keyof Config; // "host" | "port" | "ssl"
type HostType = Config["host"]; // string
// Mapped Types with Template Literals
type EventConfig<T extends string> = {
[K in T as `on${Capitalize<K>}`]: (event: K) => void;
};
// Example: EventConfig<"click" | "hover">
// Result: { onClick: (event: "click") => void; onHover: (event: "hover") => void; }
// Recursive Types
type TreeNode<T> = {
value: T;
children?: TreeNode<T>[];
};
// Deep Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Deep Readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// Branded Types
type Brand<T, B> = T & { __brand: B };
type USD = Brand<number, "USD">;
type EUR = Brand<number, "EUR">;
const usd = 10 as USD;
const eur = 10 as EUR;
function convertUSDtoEUR(usd: USD): EUR {
return (usd * 0.85) as EUR;
}
// Flatten Array Type
type Flatten<T> = T extends any[] ? T[number] : T;
type Str = Flatten<string[]>; // string
type Num = Flatten<number>; // number
// Merge Types
type Merge<A, B> = {
[K in keyof A | keyof B]:
K extends keyof A & keyof B ? A[K] | B[K] :
K extends keyof A ? A[K] :
K extends keyof B ? B[K] : never;
};// Custom Error Classes
class ValidationError extends Error {
constructor(message: string, public field: string) {
super(message);
this.name = "ValidationError";
}
}
class DatabaseError extends Error {
constructor(message: string, public code: number) {
super(message);
this.name = "DatabaseError";
}
}
// Error Handling with Types
async function fetchData(url: string): Promise<string> {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
} catch (error) {
if (error instanceof TypeError) {
throw new Error("Network error occurred");
}
throw error; // Re-throw unknown errors
}
}
// Type-Safe Error Handling
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function divide(a: number, b: number): Result<number, string> {
if (b === 0) {
return { success: false, error: "Division by zero" };
}
return { success: true, data: a / b };
}
// Using the result
const result = divide(10, 0);
if (result.success) {
console.log(result.data);
} else {
console.error(result.error);
}
// Async Result Pattern
async function asyncOperation(): Promise<Result<string>> {
try {
const data = await fetchData("https://api.example.com");
return { success: true, data };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error : new Error(String(error))
};
}
}
// Assertion Functions
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new Error("Not a string!");
}
}{
"compilerOptions": {
/* Basic Options */
"target": "ES2020", // Target ECMAScript version
"module": "commonjs", // Module system
"lib": ["ES2020", "DOM"], // Library files to include
"outDir": "./dist", // Output directory
"rootDir": "./src", // Source directory
"strict": true, // Enable all strict checks
/* Module Resolution */
"moduleResolution": "node", // Module resolution strategy
"baseUrl": "./", // Base directory for resolution
"paths": { // Path mapping
"@app/*": ["src/*"],
"@utils/*": ["src/utils/*"]
},
"esModuleInterop": true, // ES module interop
"allowSyntheticDefaultImports": true, // Allow default imports
/* Strict Type-Checking */
"noImplicitAny": true, // Error on implicit any
"strictNullChecks": true, // Strict null checking
"strictFunctionTypes": true, // Strict function types
"noImplicitThis": true, // Error on implicit this
"alwaysStrict": true, // Parse in strict mode
/* Additional Checks */
"noUnusedLocals": true, // Error on unused locals
"noUnusedParameters": true, // Error on unused parameters
"noImplicitReturns": true, // Error on implicit returns
"noFallthroughCasesInSwitch": true, // Error on fallthrough
/* Source Maps */
"sourceMap": true, // Generate source maps
"declaration": true, // Generate .d.ts files
"declarationMap": true, // Generate declaration map
/* Advanced */
"skipLibCheck": true, // Skip type checking of .d.ts
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true, // Allow importing JSON
"isolatedModules": true // Transpile each file separately
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"],
"extends": "./tsconfig.base.json" // Extend from base config
}// 1. Use Discriminated Unions for State
type RequestState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: Error };
// 2. Branded Types for Domain Types
type Email = Brand<string, "Email">;
type Password = Brand<string, "Password">;
function createEmail(email: string): Email {
if (!email.includes("@")) throw new Error("Invalid email");
return email as Email;
}
// 3. Builder Pattern with Types
class RequestBuilder {
private url: string = "";
private method: "GET" | "POST" = "GET";
private headers: Record<string, string> = {};
setUrl(url: string): this {
this.url = url;
return this;
}
setMethod(method: "GET" | "POST"): this {
this.method = method;
return this;
}
build(): Request {
return new Request(this.url, {
method: this.method,
headers: this.headers,
});
}
}
// 4. Type-Safe Event Emitter
type EventMap = {
click: { x: number; y: number };
keypress: { key: string };
};
class TypedEventEmitter<T extends Record<string, any>> {
private handlers = new Map<keyof T, Set<Function>>();
on<K extends keyof T>(event: K, handler: (data: T[K]) => void): void {
if (!this.handlers.has(event)) {
this.handlers.set(event, new Set());
}
this.handlers.get(event)!.add(handler);
}
emit<K extends keyof T>(event: K, data: T[K]): void {
this.handlers.get(event)?.forEach(handler => handler(data));
}
}
// 5. Type-Safe Object Property Path
type Path<T, K extends keyof T = keyof T> =
K extends string
? T[K] extends object
? `${K}.${Path<T[K]>}` | K
: K
: never;
// 6. Constrained Identity Function
const createObj = <T extends Record<string, unknown>>(obj: T) => obj;
const person = createObj({
name: "John",
age: 30,
}); // Type is preserved exactly
// 7. Type-Safe Fluent Interface
class Query<T> {
private filters: Array<(item: T) => boolean> = [];
where(predicate: (item: T) => boolean): this {
this.filters.push(predicate);
return this;
}
execute(data: T[]): T[] {
return data.filter(item =>
this.filters.every(filter => filter(item))
);
}
}# Install TypeScript
npm install -g typescript
# Compile TypeScript
tsc file.ts
tsc --watch file.ts
tsc --project tsconfig.json
# Initialize tsconfig
tsc --init
# Check types without compiling
tsc --noEmit