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

Typescript封装Firebase Firestore通用withConverter出现类型错误如何解决

解决方案

核心调整逻辑

首先新增通用类型约束,明确所有使用该转换器的领域类必须实现toFirestoreJSON方法,同时修正构造函数的类型定义,对齐业务类的入参规则。

第一步:定义通用基础类型和转换器

import { 
  CollectionReference, 
  DocumentReference, 
  QueryDocumentSnapshot, 
  SnapshotOptions, 
  DocumentData 
} from "firebase/firestore"

// 所有需要使用通用转换器的领域类必须实现该接口
interface FirestoreConvertible<D> {
  toFirestoreJSON: () => Partial<D>
}

// 领域类构造函数类型定义,对齐你现有业务类的构造入参规则
type DomainConstructor<T, D> = new (props: Partial<D> & { id?: string; itemID?: string }) => T

/**
 * 通用 Collection 转换器封装
 */
export function collectionWithConverter<T extends FirestoreConvertible<D>, D>(
  TCreator: DomainConstructor<T, D>,
  ref: CollectionReference<DocumentData>
): CollectionReference<T> {
  return ref.withConverter<T>({
    toFirestore: (obj: T) => {
      return obj.toFirestoreJSON() as DocumentData
    },
    fromFirestore: (snapshot: QueryDocumentSnapshot, options: SnapshotOptions) => {
      const data = snapshot.data(options) as D
      // 对齐原有非通用实现逻辑,自动注入id和itemID
      return new TCreator({ ...data, id: snapshot.id, itemID: snapshot.id })
    }
  })
}

/**
 * 通用 Document 转换器封装
 */
export function documentWithConverter<T extends FirestoreConvertible<D>, D>(
  TCreator: DomainConstructor<T, D>,
  ref: DocumentReference<DocumentData>
): DocumentReference<T> {
  return ref.withConverter<T>({
    toFirestore: (obj: T) => {
      return obj.toFirestoreJSON() as DocumentData
    },
    fromFirestore: (snapshot: QueryDocumentSnapshot, options: SnapshotOptions) => {
      const data = snapshot.data(options) as D
      return new TCreator({ ...data, id: snapshot.id, itemID: snapshot.id })
    }
  })
}

第二步:调整公共获取引用的方法

export function getCollectionReference<T extends FirestoreConvertible<D>, D>(
  collectionName: Collections,
  TCreator: DomainConstructor<T, D>
): CollectionReference<T> {
  return collectionWithConverter(TCreator, firestoreAccessor(collectionName))
}

export function getDocumentReference<T extends FirestoreConvertible<D>, D>(
  collectionName: Collections,
  itemID: string,
  TCreator: DomainConstructor<T, D>
): DocumentReference<T> {
  return documentWithConverter(TCreator, doc(getFirestore(), collectionName, itemID))
}

第三步:业务调用示例

不需要为每个领域类单独写转换器,直接传入类构造函数即可,原有Item类无需任何修改:

export async function getItem(itemID: string): Promise<Item | null> {
  // 传入Item构造函数和对应的Props类型即可
  const ref = getDocumentReference<Item, ItemProps>(Collections.ITEMS, itemID, Item)
  const snap = await getDoc(ref)

  if (snap.exists()) {
    return snap.data()
  }
  return null
}

原有报错修复说明

  • 报错1 'data' is declared but its value is never read:修正后TS可正确识别data在构造函数参数中的展开使用,不会再误报未使用
  • 报错2/3 类型不匹配:手动为withConverter指定泛型T,同时将toFirestore返回值显式断言为DocumentData,对齐Firestore接口要求
  • 报错4 'T' could be instantiated with an arbitrary type:通过T extends FirestoreConvertible<D>约束泛型范围,明确T必须实现toFirestoreJSON方法,避免无意义的类型传入
  • 构造函数类型不兼容:将原泛型中无入参的构造函数定义,修改为接收Partial<D>和id、itemID参数的类型,和实际业务类构造逻辑对齐

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 16:54:10