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

Node.js/TypeScript中Repository模式问题:ID由数据库自动生成

解决方案

解决这个问题的核心思路是区分「待创建的输入数据」和「已持久化的领域实体」——你的Book类型代表的是已经存入数据库、拥有合法ID的实体,而创建时传入的数据不需要包含ID,具体可以通过以下两种方式处理:

1. 用TypeScript Omit工具类型简化定义

直接在仓库接口中使用Omit<Book, 'id'>作为create方法的参数类型,既复用Book的字段定义,又自动排除id:

export interface BookRepository {
  // 输入无需id,返回带完整id的Book实体
  create(book: Omit<Book, 'id'>): Promise<Book | null>
}

在Postgres的实现里,只需将传入的name和author插入数据库,再把返回的自增ID与这两个字段组合成Book对象返回:

export class PostgresBookRepository implements BookRepository {
  async create(book: Omit<Book, 'id'>): Promise<Book | null> {
    const result = await pool.query(
      'INSERT INTO books (name, author) VALUES ($1, $2) RETURNING id, name, author',
      [book.name, book.author]
    )
    return result.rows[0] || null
  }
}

2. 显式定义创建输入类型(可读性更强)

如果追求代码的直观性,可以单独定义一个用于创建的输入类型,同样基于Book排除id:

// 专属创建的输入类型,不含id字段
export type CreateBookInput = Omit<Book, 'id'>

export interface BookRepository {
  create(book: CreateBookInput): Promise<Book | null>
}

这种方式在团队协作中更清晰,其他开发者能一眼明确创建操作需要的参数。

注意:不要把Book中的id改成可选属性(id?: number),这会破坏领域实体ID必填的正确性;也不建议用类型断言强行转换无id的对象为Book,会引入类型不安全的风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:00:03