A comprehensive guide to TypeScript concepts with practical examples.
1. Basic Types
Understanding TypeScript's basic type system.
// String
let name: string = "John";
// Number
let age: number = 30;
let price: number = 19.99;
// Boolean
let isActive: boolean = true;
// Array
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ["Alice", "Bob"];
// Tuple
let person: [string, number] = ["John", 30];
// Enum
enum Color {
Red,
Green,
Blue
}
let favoriteColor: Color = Color.Blue;
// Any
let dynamicValue: any = "Hello";
dynamicValue = 42;
// Unknown
let userInput: unknown = "Hello";
if (typeof userInput === "string") {
console.log(userInput.toUpperCase());
}
// Void
function logMessage(message: string): void {
console.log(message);
}
// Never
function throwError(message: string): never {
throw new Error(message);
}
2. Control Flow
Understanding TypeScript's control flow analysis.
// Type narrowing with if statements
function processValue(value: string | number) {
if (typeof value === "string") {
// TypeScript knows value is string here
console.log(value.toUpperCase());
} else {
// TypeScript knows value is number here
console.log(value.toFixed(2));
}
}
// Type guards
function isString(value: unknown): value is string {
return typeof value === "string";
}
// Type narrowing with type guards
function processValueWithGuard(value: unknown) {
if (isString(value)) {
// TypeScript knows value is string here
console.log(value.toUpperCase());
}
}
// Type narrowing with instanceof
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {
bark() {
return "Woof!";
}
}
function processAnimal(animal: Animal) {
if (animal instanceof Dog) {
// TypeScript knows animal is Dog here
console.log(animal.bark());
}
}
3. Loops and Iteration
Understanding TypeScript's loop and iteration features.
// For...of loop with type inference
const numbers: number[] = [1, 2, 3, 4, 5];
for (const num of numbers) {
// TypeScript knows num is number
console.log(num.toFixed(2));
}
// For...in loop with type safety
interface Person {
name: string;
age: number;
}
const person: Person = { name: "John", age: 30 };
for (const key in person) {
// TypeScript knows key is keyof Person
console.log(person[key as keyof Person]);
}
// Array methods with type inference
const doubled = numbers.map(num => num * 2);
const evenNumbers = numbers.filter(num => num % 2 === 0);
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
// Iterating over objects
const entries = Object.entries(person);
for (const [key, value] of entries) {
console.log(key, value);
}
4. Functions
Understanding TypeScript's function features.
// Function type declarations
type MathOperation = (x: number, y: number) => number;
const add: MathOperation = (x, y) => x + y;
const subtract: MathOperation = (x, y) => x - y;
// Optional and default parameters
function greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((acc, curr) => acc + curr, 0);
}
// Function overloads
function process(x: number): number;
function process(x: string): string;
function process(x: number | string): number | string {
return typeof x === "number" ? x * 2 : x.toUpperCase();
}
// Generic functions
function identity<T>(arg: T): T {
return arg;
}
// Async functions
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json();
}
5. Objects and Interfaces
Understanding TypeScript's object and interface features.
// Interface definition
interface User {
id: number;
name: string;
email: string;
age?: number; // Optional property
readonly createdAt: Date; // Read-only property
}
// Implementing an interface
const user: User = {
id: 1,
name: "John Doe",
email: "john@example.com",
createdAt: new Date()
};
// Extending interfaces
interface Employee extends User {
department: string;
salary: number;
}
// Type aliases
type Point = {
x: number;
y: number;
};
// Intersection types
type EmployeeWithContact = Employee & {
phone: string;
address: string;
};
// Index signatures
interface StringMap {
[key: string]: string;
}
// Mapped types
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
6. Classes and Inheritance
Understanding TypeScript's class and inheritance features.
// Class definition
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
// Inheritance
class Dog extends Animal {
bark() {
console.log("Woof! Woof!");
}
}
// Abstract classes
abstract class Vehicle {
abstract start(): void;
abstract stop(): void;
}
class Car extends Vehicle {
start() {
console.log("Starting car...");
}
stop() {
console.log("Stopping car...");
}
}
// Access modifiers
class BankAccount {
private balance: number;
public readonly accountNumber: string;
constructor(accountNumber: string, initialBalance: number) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public deposit(amount: number): void {
this.balance += amount;
}
public getBalance(): number {
return this.balance;
}
}
7. Generics
Understanding TypeScript's generic features.
// Generic function
function identity<T>(arg: T): T {
return arg;
}
// Generic interface
interface Container<T> {
value: T;
getValue(): T;
}
// Generic class
class Box<T> {
private content: T;
constructor(value: T) {
this.content = value;
}
getValue(): T {
return this.content;
}
}
// Generic constraints
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
// Generic type parameters
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
// Generic utility types
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
8. Error Handling
Understanding TypeScript's error handling features.
// Custom error class
class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = "ValidationError";
}
}
// Try-catch with type narrowing
function parseJSON(json: string): unknown {
try {
return JSON.parse(json);
} catch (error) {
if (error instanceof Error) {
throw new ValidationError(`Invalid JSON: ${error.message}`);
}
throw error;
}
}
// Error handling with async/await
async function fetchData(url: string): Promise<unknown> {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
console.error("Error fetching data:", error.message);
}
throw error;
}
}
// Type guards for error handling
function isError(error: unknown): error is Error {
return error instanceof Error;
}
// Error handling with Result type
type Result<T, E = Error> = {
success: true;
data: T;
} | {
success: false;
error: E;
};
function safeOperation<T>(operation: () => T): Result<T> {
try {
const data = operation();
return { success: true, data };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error : new Error(String(error))
};
}
}
9. Async/Await
Understanding TypeScript's async/await features.
// Async function with type annotations
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`Failed to fetch user: ${response.statusText}`);
}
return response.json();
}
// Promise.all with type inference
async function fetchMultipleUsers(ids: number[]): Promise<User[]> {
const promises = ids.map(id => fetchUser(id));
return Promise.all(promises);
}
// Async error handling
async function safeFetch<T>(url: string): Promise<T> {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
} catch (error) {
console.error("Fetch error:", error);
throw error;
}
}
// Async function with timeout
async function fetchWithTimeout<T>(
url: string,
timeout: number
): Promise<T> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return response.json();
} catch (error) {
clearTimeout(timeoutId);
if (error instanceof Error) {
throw new Error(`Request failed: ${error.message}`);
}
throw error;
}
}
// Async function with retry logic
async function fetchWithRetry<T>(
url: string,
retries: number = 3
): Promise<T> {
for (let i = 0; i < retries; i++) {
try {
return await fetchWithTimeout<T>(url, 5000);
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
throw new Error("All retries failed");
}
10. Type Guards and Type Assertions
Understanding TypeScript's type guards and assertions.
// Type guards
function isString(value: unknown): value is string {
return typeof value === "string";
}
function isNumber(value: unknown): value is number {
return typeof value === "number";
}
// Type assertions
let value: unknown = "Hello";
let length: number = (value as string).length;
// Type predicates
interface Bird {
type: "bird";
flyingSpeed: number;
}
interface Horse {
type: "horse";
runningSpeed: number;
}
type Animal = Bird | Horse;
function isBird(animal: Animal): animal is Bird {
return animal.type === "bird";
}
// Type narrowing with instanceof
class Car {
drive() {
console.log("Driving...");
}
}
class Truck {
load() {
console.log("Loading...");
}
}
type Vehicle = Car | Truck;
function useVehicle(vehicle: Vehicle) {
if (vehicle instanceof Car) {
vehicle.drive();
} else {
vehicle.load();
}
}
// Type narrowing with in operator
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape): number {
if ("radius" in shape) {
return Math.PI * shape.radius ** 2;
} else {
return shape.sideLength ** 2;
}
}
11. Array Methods
Understanding TypeScript's array manipulation methods.
// Array methods with type safety
const numbers: number[] = [1, 2, 3, 4, 5];
// map with type inference
const doubled = numbers.map(num => num * 2);
// filter with type guards
const evenNumbers = numbers.filter((num): num is number => num % 2 === 0);
// reduce with accumulator type
const sum = numbers.reduce((acc: number, curr) => acc + curr, 0);
// find with type narrowing
const firstEven = numbers.find((num): num is number => num % 2 === 0);
// some and every with type predicates
const hasEven = numbers.some((num): num is number => num % 2 === 0);
const allPositive = numbers.every((num): num is number => num > 0);
// flatMap with type inference
const matrix: number[][] = [[1, 2], [3, 4], [5, 6]];
const flattened = matrix.flatMap(row => row);
// sort with type-safe comparison
const sorted = [...numbers].sort((a, b) => a - b);
// slice with type preservation
const middle = numbers.slice(1, 4);
// splice with type safety
const removed = [...numbers].splice(1, 2);
12. String Manipulation
Understanding TypeScript's string handling features.
// String type safety
const str: string = "Hello, TypeScript!";
// Template literals with type inference
const name: string = "John";
const greeting = `Hello, ${name}!`;
// String methods with type safety
const upper = str.toUpperCase();
const lower = str.toLowerCase();
const trimmed = str.trim();
const replaced = str.replace("TypeScript", "JavaScript");
// String splitting with type inference
const words: string[] = str.split(" ");
// String joining with type safety
const joined = words.join("-");
// String searching with type guards
const hasTypeScript = str.includes("TypeScript");
const startsWithHello = str.startsWith("Hello");
const endsWithScript = str.endsWith("Script");
// String slicing with type preservation
const substring = str.slice(0, 5);
// String padding with type safety
const padded = str.padStart(20, "*");
// String repeating with type checking
const repeated = "*".repeat(5);
13. Date and Time
Understanding TypeScript's date and time handling.
// Date type safety
const now: Date = new Date();
// Date creation with type inference
const specificDate = new Date(2024, 2, 15); // March 15, 2024
const fromString = new Date("2024-03-15");
const fromTimestamp = new Date(1710432000000);
// Date methods with type safety
const year = now.getFullYear();
const month = now.getMonth();
const day = now.getDate();
const hours = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
// Date formatting with type preservation
const formatted = now.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
// Date arithmetic with type safety
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
// Date comparison with type guards
const isFuture = (date: Date): boolean => date > now;
const isPast = (date: Date): boolean => date < now;
// Date difference calculation
function getDaysBetween(date1: Date, date2: Date): number {
const diffTime = Math.abs(date2.getTime() - date1.getTime());
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
}
14. Regular Expressions
Understanding TypeScript's regex features.
// Regex type safety
const emailRegex: RegExp = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
// Regex creation with type inference
const phoneRegex = new RegExp("^\+?[1-9]\d{1,14}$");
const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
// Regex testing with type guards
function isValidEmail(email: string): boolean {
return emailRegex.test(email);
}
// Regex matching with type inference
function extractPhoneNumbers(text: string): string[] {
const matches = text.match(/\+?[1-9]\d{1,14}/g) || [];
return matches;
}
// Regex replacement with type safety
function maskEmail(email: string): string {
return email.replace(/(?<=.{3}).(?=.*@)/g, "*");
}
// Regex groups with type preservation
function parseDate(dateStr: string): { year: number; month: number; day: number } | null {
const match = dateStr.match(/(\d{4})-(\d{2})-(\d{2})/);
if (!match) return null;
return {
year: parseInt(match[1]),
month: parseInt(match[2]),
day: parseInt(match[3])
};
}
// Regex flags with type safety
const globalRegex = /pattern/g;
const caseInsensitiveRegex = /pattern/i;
const multilineRegex = /pattern/m;
// Regex with Unicode support
const unicodeRegex = /\p{Emoji}/u;
15. Working with APIs
Understanding TypeScript's API integration features.
// API response types
interface User {
id: number;
name: string;
email: string;
}
interface Post {
id: number;
title: string;
body: string;
userId: number;
}
// API client with type safety
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async get<T>(endpoint: string): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
async post<T>(endpoint: string, data: unknown): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
}
// API usage with type inference
const api = new ApiClient("https://api.example.com");
async function fetchUser(id: number): Promise<User> {
return api.get<User>(`/users/${id}`);
}
async function createPost(post: Omit<Post, "id">): Promise<Post> {
return api.post<Post>("/posts", post);
}
// API error handling with type guards
interface ApiError {
code: string;
message: string;
}
function isApiError(error: unknown): error is ApiError {
return (
typeof error === "object" &&
error !== null &&
"code" in error &&
"message" in error
);
}
// API response caching with type safety
class Cache<T> {
private cache: Map<string, { data: T; timestamp: number }>;
private ttl: number;
constructor(ttl: number) {
this.cache = new Map();
this.ttl = ttl;
}
set(key: string, data: T): void {
this.cache.set(key, { data, timestamp: Date.now() });
}
get(key: string): T | null {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() - item.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return item.data;
}
}
// Type-safe API response
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
async function fetchJson<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
const data = await response.json();
return {
data,
status: response.status,
message: response.statusText
};
}
16. State Management
Understanding TypeScript's state management patterns.
// State management with type safety
interface State {
count: number;
todos: Todo[];
filter: "all" | "active" | "completed";
}
interface Todo {
id: number;
text: string;
completed: boolean;
}
// Action types with discriminated unions
type Action =
| { type: "INCREMENT" }
| { type: "DECREMENT" }
| { type: "ADD_TODO"; payload: string }
| { type: "TOGGLE_TODO"; payload: number }
| { type: "SET_FILTER"; payload: State["filter"] };
// Reducer with type safety
function reducer(state: State, action: Action): State {
switch (action.type) {
case "INCREMENT":
return { ...state, count: state.count + 1 };
case "DECREMENT":
return { ...state, count: state.count - 1 };
case "ADD_TODO":
return {
...state,
todos: [
...state.todos,
{
id: Date.now(),
text: action.payload,
completed: false
}
]
};
case "TOGGLE_TODO":
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
case "SET_FILTER":
return { ...state, filter: action.payload };
default:
return state;
}
}
// Store with type safety
class Store<T> {
private state: T;
private listeners: Set<(state: T) => void>;
constructor(initialState: T) {
this.state = initialState;
this.listeners = new Set();
}
getState(): T {
return this.state;
}
setState(newState: T): void {
this.state = newState;
this.notify();
}
subscribe(listener: (state: T) => void): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
private notify(): void {
this.listeners.forEach(listener => listener(this.state));
}
}
// Usage with type inference
const initialState: State = {
count: 0,
todos: [],
filter: "all"
};
const store = new Store<State>(initialState);
// Action creators with type safety
const increment = (): Action => ({ type: "INCREMENT" });
const decrement = (): Action => ({ type: "DECREMENT" });
const addTodo = (text: string): Action => ({ type: "ADD_TODO", payload: text });
const toggleTodo = (id: number): Action => ({ type: "TOGGLE_TODO", payload: id });
const setFilter = (filter: State["filter"]): Action => ({ type: "SET_FILTER", payload: filter });
17. Testing
Understanding TypeScript's testing features.
// Test types with Jest
import { describe, it, expect, beforeEach } from "@jest/globals";
// Type-safe test suite
describe("Calculator", () => {
let calculator: Calculator;
beforeEach(() => {
calculator = new Calculator();
});
it("should add two numbers", () => {
const result = calculator.add(2, 3);
expect(result).toBe(5);
});
it("should subtract two numbers", () => {
const result = calculator.subtract(5, 3);
expect(result).toBe(2);
});
});
// Type-safe test utilities
function createTestUser(overrides?: Partial<User>): User {
return {
id: 1,
name: "Test User",
email: "test@example.com",
...overrides
};
}
// Type-safe mock functions
type MockFunction<T extends (...args: any[]) => any> = jest.Mock<ReturnType<T>, Parameters<T>>;
function createMockApi<T>(): MockFunction<() => Promise<T>> {
return jest.fn();
}
// Type-safe test data
interface TestData {
input: number;
expected: number;
}
const testCases: TestData[] = [
{ input: 2, expected: 4 },
{ input: 3, expected: 6 },
{ input: 4, expected: 8 }
];
// Type-safe test helpers
function assertType<T>(value: T): void {
// This function helps TypeScript infer types in tests
expect(value).toBeDefined();
}
// Type-safe async tests
async function testAsyncOperation<T>(
operation: () => Promise<T>,
expected: T
): Promise<void> {
const result = await operation();
expect(result).toEqual(expected);
}
// Type-safe test matchers
expect.extend({
toBeWithinRange(received: number, floor: number, ceiling: number) {
const pass = received >= floor && received <= ceiling;
if (pass) {
return {
message: () =>
`expected ${received} not to be within range ${floor} - ${ceiling}`,
pass: true
};
} else {
return {
message: () =>
`expected ${received} to be within range ${floor} - ${ceiling}`,
pass: false
};
}
}
});
18. Debugging
Understanding TypeScript's debugging features.
// Debug types with type safety
interface DebugConfig {
enabled: boolean;
level: "info" | "warn" | "error";
prefix?: string;
}
// Type-safe debug logger
class DebugLogger {
private config: DebugConfig;
constructor(config: DebugConfig) {
this.config = config;
}
log(message: string, data?: unknown): void {
if (!this.config.enabled) return;
console.log(`${this.config.prefix || ""} ${message}`, data);
}
warn(message: string, data?: unknown): void {
if (!this.config.enabled || this.config.level === "info") return;
console.warn(`${this.config.prefix || ""} ${message}`, data);
}
error(message: string, data?: unknown): void {
if (!this.config.enabled || this.config.level === "info" || this.config.level === "warn") return;
console.error(`${this.config.prefix || ""} ${message}`, data);
}
}
// Type-safe debug utilities
function debugValue<T>(value: T, label?: string): T {
console.log(label ? `${label}:` : "Debug:", value);
return value;
}
// Type-safe debug assertions
function assertType<T>(value: unknown, type: string): asserts value is T {
if (typeof value !== type) {
throw new Error(`Expected ${type}, got ${typeof value}`);
}
}
// Type-safe debug timing
function measureTime<T>(fn: () => T): T {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(`Execution time: ${end - start}ms`);
return result;
}
// Type-safe debug memory
function logMemoryUsage(): void {
if (typeof process !== "undefined") {
const used = process.memoryUsage();
console.log("Memory usage:", {
heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
external: `${Math.round(used.external / 1024 / 1024)}MB`
});
}
}
// Type-safe debug stack traces
function getStackTrace(): string {
const stack = new Error().stack;
return stack ? stack.split("\n").slice(2).join("\n") : "";
}
19. Performance Optimization
Understanding TypeScript's performance optimization features.
// Performance types with type safety
interface PerformanceMetrics {
startTime: number;
endTime: number;
duration: number;
memory?: {
heapUsed: number;
heapTotal: number;
};
}
// Type-safe performance measurement
class PerformanceMonitor {
private metrics: Map<string, PerformanceMetrics>;
constructor() {
this.metrics = new Map();
}
start(label: string): void {
this.metrics.set(label, {
startTime: performance.now(),
endTime: 0,
duration: 0
});
}
end(label: string): PerformanceMetrics {
const metric = this.metrics.get(label);
if (!metric) {
throw new Error(`No metric found for label: ${label}`);
}
metric.endTime = performance.now();
metric.duration = metric.endTime - metric.startTime;
if (typeof process !== "undefined") {
const memory = process.memoryUsage();
metric.memory = {
heapUsed: memory.heapUsed,
heapTotal: memory.heapTotal
};
}
return metric;
}
}
// Type-safe memoization
function memoize<T extends (...args: any[]) => any>(
fn: T,
keyFn: (...args: Parameters<T>) => string = (...args) => JSON.stringify(args)
): T {
const cache = new Map<string, ReturnType<T>>();
return ((...args: Parameters<T>): ReturnType<T> => {
const key = keyFn(...args);
if (cache.has(key)) {
return cache.get(key)!;
}
const result = fn(...args);
cache.set(key, result);
return result;
}) as T;
}
// Type-safe debouncing
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: NodeJS.Timeout;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
// Type-safe throttling
function throttle<T extends (...args: any[]) => any>(
fn: T,
limit: number
): (...args: Parameters<T>) => void {
let inThrottle: boolean;
let lastResult: ReturnType<T>;
return (...args: Parameters<T>) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
// Type-safe lazy loading
class LazyLoader<T> {
private value: T | null = null;
private loader: () => Promise<T>;
constructor(loader: () => Promise<T>) {
this.loader = loader;
}
async getValue(): Promise<T> {
if (!this.value) {
this.value = await this.loader();
}
return this.value;
}
}
20. Best Practices
Understanding TypeScript's best practices.
// Type-safe configuration
interface Config {
apiUrl: string;
timeout: number;
retries: number;
features: {
darkMode: boolean;
notifications: boolean;
};
}
// Type-safe environment variables
const config: Config = {
apiUrl: process.env.API_URL || "https://api.example.com",
timeout: parseInt(process.env.TIMEOUT || "5000", 10),
retries: parseInt(process.env.RETRIES || "3", 10),
features: {
darkMode: process.env.DARK_MODE === "true",
notifications: process.env.NOTIFICATIONS === "true"
}
};
// Type-safe error handling
class AppError extends Error {
constructor(
message: string,
public code: string,
public status: number
) {
super(message);
this.name = "AppError";
}
}
// Type-safe validation
function validateConfig(config: unknown): asserts config is Config {
if (
typeof config !== "object" ||
config === null ||
!("apiUrl" in config) ||
!("timeout" in config) ||
!("retries" in config) ||
!("features" in config)
) {
throw new AppError("Invalid configuration", "INVALID_CONFIG", 400);
}
}
// Type-safe dependency injection
interface Service {
getData(): Promise<unknown>;
}
class ServiceImpl implements Service {
async getData(): Promise<unknown> {
return { data: "example" };
}
}
class Container {
private services: Map<string, Service>;
constructor() {
this.services = new Map();
}
register(name: string, service: Service): void {
this.services.set(name, service);
}
get(name: string): Service {
const service = this.services.get(name);
if (!service) {
throw new AppError(`Service not found: ${name}`, "SERVICE_NOT_FOUND", 404);
}
return service;
}
}
// Type-safe logging
interface Logger {
info(message: string, ...args: unknown[]): void;
warn(message: string, ...args: unknown[]): void;
error(message: string, ...args: unknown[]): void;
}
class ConsoleLogger implements Logger {
info(message: string, ...args: unknown[]): void {
console.log(`[INFO] ${message}`, ...args);
}
warn(message: string, ...args: unknown[]): void {
console.warn(`[WARN] ${message}`, ...args);
}
error(message: string, ...args: unknown[]): void {
console.error(`[ERROR] ${message}`, ...args);
}
}
21. Modules and Imports
Understanding TypeScript's module system.
// Module exports with type safety
export interface User {
id: number;
name: string;
}
export type UserRole = "admin" | "user" | "guest";
export class UserService {
private users: User[] = [];
addUser(user: User): void {
this.users.push(user);
}
getUser(id: number): User | undefined {
return this.users.find(user => user.id === id);
}
}
// Default exports with type safety
export default class AuthService {
private currentUser: User | null = null;
login(user: User): void {
this.currentUser = user;
}
logout(): void {
this.currentUser = null;
}
getCurrentUser(): User | null {
return this.currentUser;
}
}
// Re-exports with type preservation
export { UserService as UserManager } from "./user-service";
export type { User as UserType } from "./user-service";
// Dynamic imports with type safety
async function loadModule<T>(path: string): Promise<T> {
const module = await import(path);
return module.default;
}
// Namespace exports with type safety
export namespace Utils {
export function formatDate(date: Date): string {
return date.toLocaleDateString();
}
export function formatCurrency(amount: number): string {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
}).format(amount);
}
}
22. Working with JSON
Understanding TypeScript's JSON handling features.
// JSON type safety
interface User {
id: number;
name: string;
email: string;
}
// Type-safe JSON parsing
function parseUser(json: string): User {
const parsed = JSON.parse(json);
if (!isUser(parsed)) {
throw new Error("Invalid user data");
}
return parsed;
}
// Type guard for JSON validation
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
typeof value.id === "number" &&
"name" in value &&
typeof value.name === "string" &&
"email" in value &&
typeof value.email === "string"
);
}
// Type-safe JSON stringification
function stringifyUser(user: User): string {
return JSON.stringify(user, null, 2);
}
// Type-safe JSON transformation
function transformUser(user: User): Omit<User, "email"> {
const { email, ...rest } = user;
return rest;
}
// Type-safe JSON schema validation
interface Schema {
type: string;
properties: Record<string, unknown>;
required: string[];
}
function validateAgainstSchema<T>(data: unknown, schema: Schema): data is T {
// Implementation of schema validation
return true;
}
// Type-safe JSON API response
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
async function fetchJson<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
const data = await response.json();
return {
data,
status: response.status,
message: response.statusText
};
}
23. File Operations
Understanding TypeScript's file handling features.
// File types with type safety
interface FileMetadata {
name: string;
size: number;
type: string;
lastModified: Date;
}
// Type-safe file reading
async function readFile(path: string): Promise<string> {
const response = await fetch(path);
if (!response.ok) {
throw new Error(`Failed to read file: ${response.statusText}`);
}
return response.text();
}
// Type-safe file writing
async function writeFile(path: string, content: string): Promise<void> {
const blob = new Blob([content], { type: "text/plain" });
const response = await fetch(path, {
method: "PUT",
body: blob
});
if (!response.ok) {
throw new Error(`Failed to write file: ${response.statusText}`);
}
}
// Type-safe file operations
class FileManager {
private files: Map<string, FileMetadata>;
constructor() {
this.files = new Map();
}
async addFile(file: File): Promise<void> {
this.files.set(file.name, {
name: file.name,
size: file.size,
type: file.type,
lastModified: new Date(file.lastModified)
});
}
getFileMetadata(name: string): FileMetadata | undefined {
return this.files.get(name);
}
async deleteFile(name: string): Promise<void> {
this.files.delete(name);
}
}
// Type-safe file upload
interface UploadProgress {
loaded: number;
total: number;
percentage: number;
}
async function uploadFile(
file: File,
onProgress?: (progress: UploadProgress) => void
): Promise<void> {
const formData = new FormData();
formData.append("file", file);
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", event => {
if (event.lengthComputable && onProgress) {
onProgress({
loaded: event.loaded,
total: event.total,
percentage: (event.loaded / event.total) * 100
});
}
});
return new Promise((resolve, reject) => {
xhr.onload = () => resolve();
xhr.onerror = () => reject(new Error("Upload failed"));
xhr.open("POST", "/upload");
xhr.send(formData);
});
}
24. Event Handling
Understanding TypeScript's event handling features.
// Event types with type safety
interface EventMap {
click: MouseEvent;
keydown: KeyboardEvent;
submit: Event;
custom: CustomEvent;
}
// Type-safe event emitter
class EventEmitter<T extends EventMap> {
private listeners: Map<keyof T, Set<(event: T[keyof T]) => void>>;
constructor() {
this.listeners = new Map();
}
on<K extends keyof T>(event: K, listener: (event: T[K]) => void): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event)!.add(listener as any);
}
off<K extends keyof T>(event: K, listener: (event: T[K]) => void): void {
this.listeners.get(event)?.delete(listener as any);
}
emit<K extends keyof T>(event: K, data: T[K]): void {
this.listeners.get(event)?.forEach(listener => listener(data));
}
}
// Type-safe event handling
class Button {
private emitter: EventEmitter<EventMap>;
constructor() {
this.emitter = new EventEmitter();
}
onClick(listener: (event: MouseEvent) => void): void {
this.emitter.on("click", listener);
}
onKeyDown(listener: (event: KeyboardEvent) => void): void {
this.emitter.on("keydown", listener);
}
triggerClick(event: MouseEvent): void {
this.emitter.emit("click", event);
}
}
// Type-safe custom events
interface CustomEventData {
detail: unknown;
}
class CustomEventEmitter extends EventEmitter<EventMap> {
emitCustom(data: CustomEventData): void {
this.emit("custom", new CustomEvent("custom", { detail: data }));
}
}
// Type-safe event delegation
function delegateEvent<T extends Event>(
element: HTMLElement,
selector: string,
event: string,
handler: (event: T, target: HTMLElement) => void
): void {
element.addEventListener(event, (e: Event) => {
const target = (e.target as HTMLElement).closest(selector);
if (target) {
handler(e as T, target);
}
});
}
25. DOM Manipulation
Understanding TypeScript's DOM manipulation features.
// DOM types with type safety
interface ElementAttributes {
id?: string;
class?: string;
style?: Partial<CSSStyleDeclaration>;
[key: string]: string | undefined;
}
// Type-safe element creation
function createElement<K extends keyof HTMLElementTagNameMap>(
tag: K,
attributes?: ElementAttributes,
children?: (string | Node)[]
): HTMLElementTagNameMap[K] {
const element = document.createElement(tag);
if (attributes) {
Object.entries(attributes).forEach(([key, value]) => {
if (value !== undefined) {
element.setAttribute(key, value);
}
});
}
if (children) {
children.forEach(child => {
if (typeof child === "string") {
element.appendChild(document.createTextNode(child));
} else {
element.appendChild(child);
}
});
}
return element;
}
// Type-safe element selection
function selectElement<K extends keyof HTMLElementTagNameMap>(
selector: K
): HTMLElementTagNameMap[K] | null {
return document.querySelector(selector);
}
// Type-safe element manipulation
class ElementManager {
private element: HTMLElement;
constructor(element: HTMLElement) {
this.element = element;
}
setText(text: string): void {
this.element.textContent = text;
}
setHTML(html: string): void {
this.element.innerHTML = html;
}
addClass(className: string): void {
this.element.classList.add(className);
}
removeClass(className: string): void {
this.element.classList.remove(className);
}
setStyle(style: Partial<CSSStyleDeclaration>): void {
Object.assign(this.element.style, style);
}
}
// Type-safe event binding
function bindEvent<K extends keyof HTMLElementEventMap>(
element: HTMLElement,
event: K,
handler: (event: HTMLElementEventMap[K]) => void
): void {
element.addEventListener(event, handler);
}
// Type-safe element animation
interface AnimationOptions {
duration: number;
easing: string;
delay?: number;
}
function animateElement(
element: HTMLElement,
properties: Partial<CSSStyleDeclaration>,
options: AnimationOptions
): Promise<void> {
return new Promise(resolve => {
const animation = element.animate(
[element.style, properties],
{
duration: options.duration,
easing: options.easing,
delay: options.delay
}
);
animation.onfinish = () => resolve();
});
}
26. Form Handling
Understanding TypeScript's form handling features.
// Form types with type safety
interface FormData {
[key: string]: string | number | boolean | File | null;
}
interface ValidationRule {
required?: boolean;
minLength?: number;
maxLength?: number;
pattern?: RegExp;
custom?: (value: unknown) => boolean;
}
// Type-safe form validation
class FormValidator {
private rules: Map<string, ValidationRule>;
constructor() {
this.rules = new Map();
}
addRule(field: string, rule: ValidationRule): void {
this.rules.set(field, rule);
}
validate(data: FormData): Map<string, string[]> {
const errors = new Map<string, string[]>();
for (const [field, rule] of this.rules) {
const value = data[field];
const fieldErrors: string[] = [];
if (rule.required && !value) {
fieldErrors.push("This field is required");
}
if (typeof value === "string") {
if (rule.minLength && value.length < rule.minLength) {
fieldErrors.push(`Minimum length is ${rule.minLength}`);
}
if (rule.maxLength && value.length > rule.maxLength) {
fieldErrors.push(`Maximum length is ${rule.maxLength}`);
}
if (rule.pattern && !rule.pattern.test(value)) {
fieldErrors.push("Invalid format");
}
}
if (rule.custom && !rule.custom(value)) {
fieldErrors.push("Invalid value");
}
if (fieldErrors.length > 0) {
errors.set(field, fieldErrors);
}
}
return errors;
}
}
// Type-safe form handling
class FormHandler {
private form: HTMLFormElement;
private validator: FormValidator;
constructor(form: HTMLFormElement) {
this.form = form;
this.validator = new FormValidator();
}
addValidation(field: string, rule: ValidationRule): void {
this.validator.addRule(field, rule);
}
async handleSubmit(
onSubmit: (data: FormData) => Promise<void>
): Promise<void> {
this.form.addEventListener("submit", async event => {
event.preventDefault();
const formData = new FormData(this.form);
const data: FormData = {};
for (const [key, value] of formData.entries()) {
data[key] = value;
}
const errors = this.validator.validate(data);
if (errors.size > 0) {
this.displayErrors(errors);
return;
}
try {
await onSubmit(data);
this.form.reset();
} catch (error) {
console.error("Form submission failed:", error);
}
});
}
private displayErrors(errors: Map<string, string[]>): void {
for (const [field, fieldErrors] of errors) {
const element = this.form.elements.namedItem(field) as HTMLElement;
if (element) {
const errorElement = document.createElement("div");
errorElement.className = "error-message";
errorElement.textContent = fieldErrors.join(", ");
element.parentNode?.appendChild(errorElement);
}
}
}
}
// Type-safe form field
class FormField<T> {
private value: T;
private onChange: (value: T) => void;
constructor(initialValue: T, onChange: (value: T) => void) {
this.value = initialValue;
this.onChange = onChange;
}
setValue(value: T): void {
this.value = value;
this.onChange(value);
}
getValue(): T {
return this.value;
}
}
27. Local Storage
Understanding TypeScript's local storage features.
// Local storage types with type safety
interface StorageItem<T> {
key: string;
value: T;
timestamp: number;
}
// Type-safe local storage manager
class LocalStorageManager {
private prefix: string;
constructor(prefix: string = "app_") {
this.prefix = prefix;
}
private getKey(key: string): string {
return `${this.prefix}${key}`;
}
set<T>(key: string, value: T): void {
const item: StorageItem<T> = {
key,
value,
timestamp: Date.now()
};
localStorage.setItem(this.getKey(key), JSON.stringify(item));
}
get<T>(key: string): T | null {
const item = localStorage.getItem(this.getKey(key));
if (!item) return null;
try {
const parsed = JSON.parse(item) as StorageItem<T>;
return parsed.value;
} catch {
return null;
}
}
remove(key: string): void {
localStorage.removeItem(this.getKey(key));
}
clear(): void {
Object.keys(localStorage)
.filter(key => key.startsWith(this.prefix))
.forEach(key => localStorage.removeItem(key));
}
getAll<T>(): StorageItem<T>[] {
return Object.keys(localStorage)
.filter(key => key.startsWith(this.prefix))
.map(key => {
try {
return JSON.parse(localStorage.getItem(key)!) as StorageItem<T>;
} catch {
return null;
}
})
.filter((item): item is StorageItem<T> => item !== null);
}
has(key: string): boolean {
return localStorage.getItem(this.getKey(key)) !== null;
}
getSize(): number {
return Object.keys(localStorage)
.filter(key => key.startsWith(this.prefix))
.reduce((size, key) => size + localStorage.getItem(key)!.length, 0);
}
}
// Type-safe local storage with expiration
class ExpiringLocalStorage extends LocalStorageManager {
setWithExpiry<T>(key: string, value: T, expiryInMinutes: number): void {
const item = {
value,
expiry: Date.now() + expiryInMinutes * 60 * 1000
};
this.set(key, item);
}
getWithExpiry<T>(key: string): T | null {
const item = this.get<{ value: T; expiry: number }>(key);
if (!item) return null;
if (Date.now() > item.expiry) {
this.remove(key);
return null;
}
return item.value;
}
}
// Type-safe local storage with encryption
class EncryptedLocalStorage extends LocalStorageManager {
private encryptionKey: string;
constructor(prefix: string, encryptionKey: string) {
super(prefix);
this.encryptionKey = encryptionKey;
}
private encrypt(data: string): string {
// Implementation of encryption
return data;
}
private decrypt(data: string): string {
// Implementation of decryption
return data;
}
set<T>(key: string, value: T): void {
const encrypted = this.encrypt(JSON.stringify(value));
super.set(key, encrypted);
}
get<T>(key: string): T | null {
const encrypted = super.get<string>(key);
if (!encrypted) return null;
try {
return JSON.parse(this.decrypt(encrypted)) as T;
} catch {
return null;
}
}
}
28. Session Storage
Understanding TypeScript's session storage features.
// Session storage types with type safety
interface SessionItem<T> {
key: string;
value: T;
timestamp: number;
}
// Type-safe session storage manager
class SessionStorageManager {
private prefix: string;
constructor(prefix: string = "app_") {
this.prefix = prefix;
}
private getKey(key: string): string {
return `${this.prefix}${key}`;
}
set<T>(key: string, value: T): void {
const item: SessionItem<T> = {
key,
value,
timestamp: Date.now()
};
sessionStorage.setItem(this.getKey(key), JSON.stringify(item));
}
get<T>(key: string): T | null {
const item = sessionStorage.getItem(this.getKey(key));
if (!item) return null;
try {
const parsed = JSON.parse(item) as SessionItem<T>;
return parsed.value;
} catch {
return null;
}
}
remove(key: string): void {
sessionStorage.removeItem(this.getKey(key));
}
clear(): void {
Object.keys(sessionStorage)
.filter(key => key.startsWith(this.prefix))
.forEach(key => sessionStorage.removeItem(key));
}
getAll<T>(): SessionItem<T>[] {
return Object.keys(sessionStorage)
.filter(key => key.startsWith(this.prefix))
.map(key => {
try {
return JSON.parse(sessionStorage.getItem(key)!) as SessionItem<T>;
} catch {
return null;
}
})
.filter((item): item is SessionItem<T> => item !== null);
}
has(key: string): boolean {
return sessionStorage.getItem(this.getKey(key)) !== null;
}
getSize(): number {
return Object.keys(sessionStorage)
.filter(key => key.startsWith(this.prefix))
.reduce((size, key) => size + sessionStorage.getItem(key)!.length, 0);
}
}
// Type-safe session storage with tab isolation
class TabIsolatedSessionStorage extends SessionStorageManager {
private tabId: string;
constructor(prefix: string) {
super(prefix);
this.tabId = this.generateTabId();
}
private generateTabId(): string {
return Math.random().toString(36).substring(2);
}
private getTabKey(key: string): string {
return `${this.tabId}_${key}`;
}
set<T>(key: string, value: T): void {
super.set(this.getTabKey(key), value);
}
get<T>(key: string): T | null {
return super.get<T>(this.getTabKey(key));
}
remove(key: string): void {
super.remove(this.getTabKey(key));
}
}
// Type-safe session storage with encryption
class EncryptedSessionStorage extends SessionStorageManager {
private encryptionKey: string;
constructor(prefix: string, encryptionKey: string) {
super(prefix);
this.encryptionKey = encryptionKey;
}
private encrypt(data: string): string {
// Implementation of encryption
return data;
}
private decrypt(data: string): string {
// Implementation of decryption
return data;
}
set<T>(key: string, value: T): void {
const encrypted = this.encrypt(JSON.stringify(value));
super.set(key, encrypted);
}
get<T>(key: string): T | null {
const encrypted = super.get<string>(key);
if (!encrypted) return null;
try {
return JSON.parse(this.decrypt(encrypted)) as T;
} catch {
return null;
}
}
}
29. Cookies
Understanding TypeScript's cookie handling features.
// Cookie types with type safety
interface CookieOptions {
expires?: Date;
path?: string;
domain?: string;
secure?: boolean;
sameSite?: "strict" | "lax" | "none";
}
interface CookieItem<T> {
key: string;
value: T;
options?: CookieOptions;
}
// Type-safe cookie manager
class CookieManager {
private prefix: string;
constructor(prefix: string = "app_") {
this.prefix = prefix;
}
private getKey(key: string): string {
return `${this.prefix}${key}`;
}
set<T>(key: string, value: T, options?: CookieOptions): void {
const cookieValue = JSON.stringify(value);
const cookieOptions = this.buildCookieOptions(options);
document.cookie = `${this.getKey(key)}=${encodeURIComponent(cookieValue)}${cookieOptions}`;
}
get<T>(key: string): T | null {
const cookies = this.parseCookies();
const cookie = cookies[this.getKey(key)];
if (!cookie) return null;
try {
return JSON.parse(decodeURIComponent(cookie)) as T;
} catch {
return null;
}
}
remove(key: string): void {
this.set(key, "", { expires: new Date(0) });
}
private buildCookieOptions(options?: CookieOptions): string {
if (!options) return "";
const parts: string[] = [];
if (options.expires) {
parts.push(`expires=${options.expires.toUTCString()}`);
}
if (options.path) {
parts.push(`path=${options.path}`);
}
if (options.domain) {
parts.push(`domain=${options.domain}`);
}
if (options.secure) {
parts.push("secure");
}
if (options.sameSite) {
parts.push(`samesite=${options.sameSite}`);
}
return parts.length ? `; ${parts.join("; ")}` : "";
}
private parseCookies(): Record<string, string> {
return document.cookie.split("; ").reduce((cookies, cookie) => {
const [key, value] = cookie.split("=");
cookies[key] = value;
return cookies;
}, {} as Record<string, string>);
}
}
// Type-safe cookie with expiration
class ExpiringCookieManager extends CookieManager {
setWithExpiry<T>(key: string, value: T, expiryInDays: number): void {
const expires = new Date();
expires.setDate(expires.getDate() + expiryInDays);
this.set(key, value, { expires });
}
}
// Type-safe cookie with encryption
class EncryptedCookieManager extends CookieManager {
private encryptionKey: string;
constructor(prefix: string, encryptionKey: string) {
super(prefix);
this.encryptionKey = encryptionKey;
}
private encrypt(data: string): string {
// Implementation of encryption
return data;
}
private decrypt(data: string): string {
// Implementation of decryption
return data;
}
set<T>(key: string, value: T, options?: CookieOptions): void {
const encrypted = this.encrypt(JSON.stringify(value));
super.set(key, encrypted, options);
}
get<T>(key: string): T | null {
const encrypted = super.get<string>(key);
if (!encrypted) return null;
try {
return JSON.parse(this.decrypt(encrypted)) as T;
} catch {
return null;
}
}
}
30. Web Workers
Understanding TypeScript's web worker features.
// Web worker types with type safety
interface WorkerMessage<T> {
type: string;
payload: T;
}
interface WorkerResponse<T> {
type: string;
payload: T;
error?: string;
}
// Type-safe web worker manager
class WorkerManager {
private worker: Worker;
private messageHandlers: Map<string, (payload: unknown) => Promise<unknown>>;
constructor(workerScript: string) {
this.worker = new Worker(workerScript);
this.messageHandlers = new Map();
this.setupMessageListener();
}
private setupMessageListener(): void {
this.worker.onmessage = (event: MessageEvent<WorkerResponse<unknown>>) => {
const { type, payload, error } = event.data;
if (error) {
console.error(`Worker error: ${error}`);
return;
}
const handler = this.messageHandlers.get(type);
if (handler) {
handler(payload);
}
};
}
registerHandler<T, R>(
type: string,
handler: (payload: T) => Promise<R>
): void {
this.messageHandlers.set(type, handler as (payload: unknown) => Promise<unknown>);
}
postMessage<T>(message: WorkerMessage<T>): void {
this.worker.postMessage(message);
}
terminate(): void {
this.worker.terminate();
}
}
// Type-safe web worker implementation
class DataProcessingWorker {
private worker: Worker;
constructor() {
this.worker = new Worker("data-processing.worker.js");
}
async processData<T>(data: T[]): Promise<T[]> {
return new Promise((resolve, reject) => {
const messageHandler = (event: MessageEvent<WorkerResponse<T[]>>) => {
const { payload, error } = event.data;
if (error) {
reject(new Error(error));
return;
}
resolve(payload);
};
this.worker.onmessage = messageHandler;
this.worker.postMessage({ type: "process", payload: data });
});
}
terminate(): void {
this.worker.terminate();
}
}
// Type-safe web worker pool
class WorkerPool {
private workers: Worker[];
private currentIndex: number;
constructor(workerScript: string, poolSize: number) {
this.workers = Array.from({ length: poolSize }, () => new Worker(workerScript));
this.currentIndex = 0;
}
async executeTask<T, R>(task: T): Promise<R> {
const worker = this.getNextWorker();
return new Promise((resolve, reject) => {
const messageHandler = (event: MessageEvent<WorkerResponse<R>>) => {
const { payload, error } = event.data;
if (error) {
reject(new Error(error));
return;
}
resolve(payload);
};
worker.onmessage = messageHandler;
worker.postMessage({ type: "task", payload: task });
});
}
private getNextWorker(): Worker {
const worker = this.workers[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.workers.length;
return worker;
}
terminate(): void {
this.workers.forEach(worker => worker.terminate());
}
}
// Type-safe web worker with shared memory
class SharedMemoryWorker {
private worker: Worker;
private sharedBuffer: SharedArrayBuffer;
private sharedArray: Int32Array;
constructor(workerScript: string, bufferSize: number) {
this.worker = new Worker(workerScript);
this.sharedBuffer = new SharedArrayBuffer(bufferSize);
this.sharedArray = new Int32Array(this.sharedBuffer);
}
async processData<T>(data: T[]): Promise<T[]> {
return new Promise((resolve, reject) => {
const messageHandler = (event: MessageEvent<WorkerResponse<T[]>>) => {
const { payload, error } = event.data;
if (error) {
reject(new Error(error));
return;
}
resolve(payload);
};
this.worker.onmessage = messageHandler;
this.worker.postMessage({
type: "process",
payload: data,
sharedBuffer: this.sharedBuffer
});
});
}
getSharedArray(): Int32Array {
return this.sharedArray;
}
terminate(): void {
this.worker.terminate();
}
}
31. Service Workers
Understanding TypeScript's service worker features.
// Service worker types with type safety
interface ServiceWorkerMessage<T> {
type: string;
payload: T;
}
interface ServiceWorkerResponse<T> {
type: string;
payload: T;
error?: string;
}
// Type-safe service worker registration
class ServiceWorkerManager {
private registration: ServiceWorkerRegistration | null = null;
async register(scriptURL: string, options?: RegistrationOptions): Promise<void> {
if ("serviceWorker" in navigator) {
try {
this.registration = await navigator.serviceWorker.register(scriptURL, options);
console.log("Service Worker registered successfully");
} catch (error) {
console.error("Service Worker registration failed:", error);
throw error;
}
}
}
async unregister(): Promise<boolean> {
if (this.registration) {
return await this.registration.unregister();
}
return false;
}
async update(): Promise<void> {
if (this.registration) {
await this.registration.update();
}
}
}
// Type-safe service worker cache manager
class CacheManager {
private cacheName: string;
constructor(cacheName: string) {
this.cacheName = cacheName;
}
async addToCache(request: Request, response: Response): Promise<void> {
const cache = await caches.open(this.cacheName);
await cache.put(request, response);
}
async getFromCache(request: Request): Promise<Response | undefined> {
const cache = await caches.open(this.cacheName);
return await cache.match(request);
}
async deleteFromCache(request: Request): Promise<boolean> {
const cache = await caches.open(this.cacheName);
return await cache.delete(request);
}
async clearCache(): Promise<void> {
await caches.delete(this.cacheName);
}
}
// Type-safe service worker message handler
class ServiceWorkerMessageHandler {
private handlers: Map<string, (payload: unknown) => Promise<unknown>>;
constructor() {
this.handlers = new Map();
}
registerHandler<T, R>(
type: string,
handler: (payload: T) => Promise<R>
): void {
this.handlers.set(type, handler as (payload: unknown) => Promise<unknown>);
}
async handleMessage<T, R>(
message: ServiceWorkerMessage<T>
): Promise<ServiceWorkerResponse<R>> {
const handler = this.handlers.get(message.type);
if (!handler) {
return {
type: message.type,
payload: null as unknown as R,
error: "No handler registered for this message type"
};
}
try {
const result = await handler(message.payload);
return {
type: message.type,
payload: result as R
};
} catch (error) {
return {
type: message.type,
payload: null as unknown as R,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
}
// Type-safe service worker background sync
class BackgroundSyncManager {
private registration: ServiceWorkerRegistration;
constructor(registration: ServiceWorkerRegistration) {
this.registration = registration;
}
async registerSync(tag: string): Promise<void> {
if ("sync" in this.registration) {
await this.registration.sync.register(tag);
}
}
async getTags(): Promise<string[]> {
if ("sync" in this.registration) {
return await this.registration.sync.getTags();
}
return [];
}
}
// Type-safe service worker push notification
class PushNotificationManager {
private registration: ServiceWorkerRegistration;
constructor(registration: ServiceWorkerRegistration) {
this.registration = registration;
}
async subscribeToPush(
applicationServerKey: string
): Promise<PushSubscription | null> {
try {
const subscription = await this.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey
});
return subscription;
} catch (error) {
console.error("Failed to subscribe to push notifications:", error);
return null;
}
}
async unsubscribeFromPush(): Promise<boolean> {
const subscription = await this.registration.pushManager.getSubscription();
if (subscription) {
return await subscription.unsubscribe();
}
return false;
}
}
32. Web Sockets
Understanding TypeScript's web socket features.
// WebSocket types with type safety
interface WebSocketMessage<T> {
type: string;
payload: T;
}
interface WebSocketResponse<T> {
type: string;
payload: T;
error?: string;
}
// Type-safe WebSocket manager
class WebSocketManager {
private socket: WebSocket | null = null;
private messageHandlers: Map<string, (payload: unknown) => void>;
private reconnectAttempts: number = 0;
private maxReconnectAttempts: number = 5;
private reconnectDelay: number = 1000;
constructor(private url: string) {
this.messageHandlers = new Map();
}
connect(): void {
this.socket = new WebSocket(this.url);
this.setupEventListeners();
}
private setupEventListeners(): void {
if (!this.socket) return;
this.socket.onopen = () => {
console.log("WebSocket connected");
this.reconnectAttempts = 0;
};
this.socket.onmessage = (event: MessageEvent) => {
try {
const message = JSON.parse(event.data) as WebSocketMessage<unknown>;
const handler = this.messageHandlers.get(message.type);
if (handler) {
handler(message.payload);
}
} catch (error) {
console.error("Failed to parse WebSocket message:", error);
}
};
this.socket.onclose = () => {
console.log("WebSocket disconnected");
this.attemptReconnect();
};
this.socket.onerror = (error) => {
console.error("WebSocket error:", error);
};
}
private attemptReconnect(): void {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(`Attempting to reconnect (attempt ${this.reconnectAttempts})`);
this.connect();
}, this.reconnectDelay * this.reconnectAttempts);
}
}
registerHandler<T>(
type: string,
handler: (payload: T) => void
): void {
this.messageHandlers.set(type, handler as (payload: unknown) => void);
}
send<T>(message: WebSocketMessage<T>): void {
if (this.socket?.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
} else {
console.error("WebSocket is not connected");
}
}
close(): void {
if (this.socket) {
this.socket.close();
this.socket = null;
}
}
}
// Type-safe WebSocket with authentication
class AuthenticatedWebSocket extends WebSocketManager {
private token: string;
constructor(url: string, token: string) {
super(url);
this.token = token;
}
connect(): void {
const urlWithToken = `${this.url}?token=${this.token}`;
super.connect();
}
}
// Type-safe WebSocket with heartbeat
class HeartbeatWebSocket extends WebSocketManager {
private heartbeatInterval: number = 30000;
private heartbeatTimer: NodeJS.Timeout | null = null;
connect(): void {
super.connect();
this.startHeartbeat();
}
private startHeartbeat(): void {
this.heartbeatTimer = setInterval(() => {
this.send({ type: "heartbeat", payload: { timestamp: Date.now() } });
}, this.heartbeatInterval);
}
close(): void {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
super.close();
}
}
// Type-safe WebSocket with message queue
class QueuedWebSocket extends WebSocketManager {
private messageQueue: WebSocketMessage<unknown>[] = [];
send<T>(message: WebSocketMessage<T>): void {
if (this.socket?.readyState === WebSocket.OPEN) {
super.send(message);
} else {
this.messageQueue.push(message);
}
}
private setupEventListeners(): void {
super.setupEventListeners();
if (this.socket) {
this.socket.onopen = () => {
console.log("WebSocket connected");
this.flushMessageQueue();
};
}
}
private flushMessageQueue(): void {
while (this.messageQueue.length > 0) {
const message = this.messageQueue.shift();
if (message) {
super.send(message);
}
}
}
}