TypeScript函数式编程:PostgreSQL结果转ViewModel的fp-ts管道错误
问题解决:PostgreSQL数据转换与TaskEither类型错误修复
错误核心原因
- 类型匹配错误:
mapDepartmentViewModel的参数类型写错了——res.rows的元素是Department类型(来自QueryResult<Department>的定义),但你写成了QueryResult,直接导致类型推断混乱。 - TaskEither操作误用:
TE.traverseArray要求传入的函数返回TaskEither容器,但mapDepartmentViewModel是纯转换函数,不属于异步/错误容器类型,完全不适合用这个方法。 - 需求未对齐:你需要返回单个对象,但原代码直接处理数组,缺少单条记录的校验逻辑。
修复后的完整代码
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
相关产品推荐
相关产品推荐

