JS TS 如何将重复代码重构为具有多种参数类型的高阶函数

JS TS how to refactor repetitive code into a higher order function with multiple param types

我一直想用打字稿将以下代码重构为高阶函数,以使其更清晰、更可重用,但我发现让它工作真的很有挑战性。

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);
    }
}

基本上我想用 try catch 块将整个指标功能重构为一个实用函数,允许使用相应的参数调用它,操作,TaskModel.method 和相应的参数将是 (输入)用于创建,(查询,{},选项)用于 findOne 和(查询,更新,选项)用于 findManyAndUpdate...

到目前为止,我 运行 在正确键入所有不同参数等方面遇到了困难。

所以基本上你想重构这部分:

try {
    /* SOME OPERATION */
    timer({ ...metricsLabels, success: 'true' });

    return result;
} catch (err: any) {
    timer({ ...metricsLabels, success: 'false' });
    throw new Error(err.message);
}

您可以将它包装在一个函数中,然后将 SOME OPERATION 作为参数传入:

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);
    }
}

现在你的函数可以重写为:

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)
    );
}

使用 higher-order 函数的关键是意识到您可以将代码的 不常见 部分包装在函数中以传递给代码的公共部分。