/** * Build query string from URL parameters * * Generic, type-safe function to build properly encoded query strings * from URL parameters. Supports primitives, arrays, and optional values. * * @param params - Object containing query parameters * @returns Encoded query string (empty string if no parameters) * * @example * ```ts * buildQueryString({ category: 'serif', subsets: ['latin', 'latin-ext'] }) * // Returns: "category=serif&subsets=latin&subsets=latin-ext" * * buildQueryString({ limit: 50, page: 1 }) * // Returns: "limit=50&page=1" * * buildQueryString({}) * // Returns: "" * * buildQueryString({ search: 'hello world', active: true }) * // Returns: "search=hello%20world&active=true" * ``` */ /** * Query parameter value type * Supports primitives, arrays, and excludes null/undefined */ export type QueryParamValue = string | number | boolean | string[] | number[]; /** * Query parameters object */ export type QueryParams = Record; /** * Build query string from URL parameters * * Handles: * - Primitive values (string, number, boolean) * - Arrays (multiple values with same key) * - Optional values (excludes undefined/null) * - Proper URL encoding * * Edge cases: * - Empty object → empty string * - No parameters → empty string * - Nested objects → flattens to string representation * - Special characters → proper encoding * * @param params - Object containing query parameters * @returns Encoded query string (with "?" prefix if non-empty) */ export function buildQueryString(params: QueryParams): string { const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { // Skip undefined/null values if (value === undefined || value === null) { continue; } // Handle arrays (multiple values with same key) if (Array.isArray(value)) { for (const item of value) { if (item !== undefined && item !== null) { searchParams.append(key, String(item)); } } } else { // Handle primitives searchParams.append(key, String(value)); } } const queryString = searchParams.toString(); return queryString ? `?${queryString}` : ''; }