Quería refactorizar el siguiente código en una función de orden superior con mecanografiado para hacerlo más limpio y más reutilizable, aunque me ha resultado realmente difícil hacerlo funcionar.
import { DocumentDefinition, FilterQuery, QueryOptions, UpdateQuery } from 'mongoose'; import TaskModel, { TaskDocument } from '../models/Task.model'; import { databaseResponseTimeHistogram } from '../utils/appmetrics'; export async function createTask( input: DocumentDefinition< Omit<TaskDocument, 'createdAt' | 'updatedAt' | 'taskId' | 'isCompleted'> > ) { const metricsLabels = { operation: 'createTask' }; const timer = databaseResponseTimeHistogram.startTimer(); try { const result = TaskModel.create(input); timer({ ...metricsLabels, success: 'true' }); return result; } catch (err: any) { timer({ ...metricsLabels, success: 'false' }); throw new Error(err.message); } } export async function findTask( query: FilterQuery<TaskDocument>, options: QueryOptions = { lean: true } ) { const metricsLabels = { operation: 'findTask' }; const timer = databaseResponseTimeHistogram.startTimer(); try { const result = TaskModel.findOne(query, {}, options); timer({ ...metricsLabels, success: 'true' }); return result; } catch (err: any) { timer({ ...metricsLabels, success: 'false' }); throw new Error(err.message); } } export async function findAndUpdateTask( query: FilterQuery<TaskDocument>, update: UpdateQuery<TaskDocument>, options: QueryOptions ) { const metricsLabels = { operation: 'findTask' }; const timer = databaseResponseTimeHistogram.startTimer(); try { const result = TaskModel.findOneAndUpdate(query, update, options); timer({ ...metricsLabels, success: 'true' }); return result; } catch (err: any) { timer({ ...metricsLabels, success: 'false' }); throw new Error(err.message); } }Básicamente, quiero refactorizar toda la funcionalidad de métricas con el bloque try catch en una función de utilidad, lo que permite llamarlo con los parámetros respectivos, la operación, el TaskModel.method y los parámetros correspondientes con sería (entrada) para crear, (consulta , {}, opciones) para findOne y (query, update, options) para findManyAndUpdate...
Hasta ahora he tenido dificultades con la escritura correcta de todos los diferentes parámetros, etc.
Así que básicamente quieres refactorizar esta parte:
try { /* SOME OPERATION */ timer({ ...metricsLabels, success: 'true' }); return result; } catch (err: any) { timer({ ...metricsLabels, success: 'false' }); throw new Error(err.message); } Simplemente puede envolverlo en una función y pasar SOME OPERATION como argumento:
function withTimer (label:string, operation:Function) { const metricsLabels = { operation: label }; const timer = databaseResponseTimeHistogram.startTimer(); try { const result = operation(); timer({ ...metricsLabels, success: 'true' }); return result; } catch (err: any) { timer({ ...metricsLabels, success: 'false' }); throw new Error(err.message); } }Ahora sus funciones se pueden reescribir como:
export async function createTask( input: DocumentDefinition< Omit<TaskDocument, 'createdAt' | 'updatedAt' | 'taskId' | 'isCompleted'> > ) { return withTimer('createTask', () => TaskModel.create(input) ); } export async function findTask( query: FilterQuery<TaskDocument>, options: QueryOptions = { lean: true } ) { return withTimer('findTask', () => TaskModel.findOne(query, {}, options) ); } export async function findAndUpdateTask( query: FilterQuery<TaskDocument>, update: UpdateQuery<TaskDocument>, options: QueryOptions ) { return withTimer('findTask', () => TaskModel.findOneAndUpdate(query, update, options) ); }La clave para usar funciones de orden superior es darse cuenta de que puede envolver las partes poco comunes del código en funciones para pasarlas a las partes comunes del código.