You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript函数式编程:PostgreSQL结果转ViewModel的fp-ts管道错误

问题解决:PostgreSQL数据转换与TaskEither类型错误修复

错误核心原因

  1. 类型匹配错误:mapDepartmentViewModel的参数类型写错了——res.rows的元素是Department类型(来自QueryResult<Department>的定义),但你写成了QueryResult,直接导致类型推断混乱。
  2. TaskEither操作误用:TE.traverseArray要求传入的函数返回TaskEither容器,但mapDepartmentViewModel是纯转换函数,不属于异步/错误容器类型,完全不适合用这个方法。
  3. 需求未对齐:你需要返回单个对象,但原代码直接处理数组,缺少单条记录的校验逻辑。

修复后的完整代码

type DBError = MultipleResultsError | NoRecordsFoundError | Error;

const retrieveDepartments = async ({departmentId}: {departmentId: number}): Promise<TE.TaskEither<DBError, Department>> => {
    const client = await pool.connect();

    try {
        // PostgreSQL参数占位符是$1/$2,不是?,修正SQL语法错误
        const query = 'SELECT * FROM department where departmentId = $1';
        const res: QueryResult<Department> = await client.query(query, [departmentId]);

        return pipe(
            // 校验返回记录数:确保只有一条数据
            TE.fromPredicate(
                () => res.rows.length === 1,
                () => res.rows.length === 0 
                    ? new NoRecordsFoundError(`部门ID ${departmentId}不存在`) 
                    : new MultipleResultsError(`找到多条部门ID ${departmentId}的记录`)
            )(),
            // 校验通过后取第一条数据,执行转换(如果不需要额外转换可直接返回res.rows[0])
            TE.map(() => mapDepartmentViewModel(res.rows[0]))
        );
    } catch (e) {
        return TE.left(e as DBError);
    } finally {
        client.release();
    }
}

export type Department = {
    departmentId: number,
    departmentName: string,
    departmentSupervisorId: number,
    departmentSupervisorFirstName: string,
    departmentSupervisorLastName: string
}

// 修正参数类型:传入数据库返回的单条Department记录
export const mapDepartmentViewModel = (department: Department): Department => ({
    departmentId: department.departmentId,
    departmentName: department.departmentName,
    departmentSupervisorId: department.departmentSupervisorId,
    departmentSupervisorFirstName: department.departmentSupervisorFirstName,
    departmentSupervisorLastName: department.departmentSupervisorLastName
});

关键修复点说明

  • SQL语法修正:PostgreSQL不支持?作为参数占位符,必须用$n格式,避免运行时SQL错误。
  • 单条记录校验:用TE.fromPredicate处理记录数异常,把0条/多条的情况转化为对应的错误类型,符合函数式错误处理的规范。
  • 类型修正:把mapDepartmentViewModel的参数类型改为Department,和数据库返回的记录类型匹配,彻底消除TS2345类型错误。
  • TaskEither操作调整:用TE.map替代错误的TE.chain+TE.traverseArray——纯数据转换用map即可,chain仅用于处理返回TaskEither的异步/错误操作。

函数式编程入门提示

  • TE.map:负责转换TaskEither容器内的成功值,接收纯函数,不改变容器的错误/成功结构。
  • TE.chain:用于串联两个TaskEither操作,要求传入的函数返回TaskEither,相当于异步版的flatMap。
  • TE.traverseArray:仅适用于数组中每个元素都需要异步操作或错误处理的场景,纯数据转换完全不需要它。

内容的提问来源于stack exchange,提问作者user1790300

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 05:12:43