leadScoring.tsCultiva SaaS · Sprint MVP post-launch cleanup · Módulo: services/leadScoring.ts
processLeadData() — lógica de validación con 4 bloques if anidados. Aplicado: guard clauses con retorno anticipado.calculateScore() — múltiples responsabilidades. Extraídas 3 funciones auxiliares con nombres descriptivos.TIER_LABELS.email + domain repetida en validate(), enrich() y export(). Extraída a isValidLead().data, result, item, temp, val → renombrados a leadData, scoringResult, leadRecord.return await sin try/catch. Eliminado el wrapper async — misma Promise, sin overhead.if (cond) return true; return false; → return cond; en 4 funciones de validación.function processLeadData(data: unknown): Lead { if (data !== null && data !== undefined) { if (typeof data === 'object') { if ('email' in data) { if (isEmailValid((data as any).email)) { const result = mapToLead(data); if (result.score === undefined) { result.score = 0; } return result; } else { throw new Error('Invalid email'); } } else { throw new Error('Missing email'); } } else { throw new Error('Not an object'); } } else { throw new Error('Data is null'); } }
function processLeadData(leadData: unknown): Lead { if (leadData == null) throw new Error('Data is null'); if (typeof leadData !== 'object') throw new Error('Not an object'); if (!('email' in leadData)) throw new Error('Missing email'); if (!isEmailValid((leadData as any).email)) throw new Error('Invalid email'); const lead = mapToLead(leadData); return { ...lead, score: lead.score ?? 0 }; }
function getTierLabel(score: number): string { return score >= 90 ? 'Platinum' : score >= 75 ? 'Gold' : score >= 50 ? 'Silver' : score >= 25 ? 'Bronze' : 'Unqualified'; }
const SCORE_TIERS: Array<[number, string]> = [ [90, 'Platinum'], [75, 'Gold'], [50, 'Silver'], [25, 'Bronze'], ]; function getTierLabel(score: number): string { return (SCORE_TIERS.find(([min]) => score >= min)?.[1] ?? 'Unqualified'); }
SCORE_TIERS es ahora una constante editable desde un solo lugar — añadir un nuevo tier en el futuro es 1 línea, sin tocar la lógica.
// Innecesario: async sin try/catch async function fetchLeadEnrichment( leadId: string ): Promise<Enrichment> { return await enrichmentService.get(leadId); } // Verboso: boolean return function isHighValue(val: number): boolean { if (val >= 75 && val <= 100) { return true; } else { return false; } } function hasValidDomain(item: Lead): boolean { if (BLOCKED_DOMAINS.includes(item.domain)) { return false; } else { return true; } }
// Sin async wrapper — devuelve la misma Promise function fetchLeadEnrichment( leadId: string ): Promise<Enrichment> { return enrichmentService.get(leadId); } // Directo: la condición ES el boolean function isHighValue(score: number): boolean { return score >= 75 && score <= 100; } function hasValidDomain(lead: Lead): boolean { return !BLOCKED_DOMAINS.includes(lead.domain); }
val → score e item → lead eliminan la ambigüedad semántica. El lector sabe inmediatamente de qué trata la función.
tsc --noEmit)src/services/)