如何自定义firebase DocumentData 接口?

Posted

技术标签:

【中文标题】如何自定义firebase DocumentData 接口?【英文标题】:How to customise firebase DocumentData interface? 【发布时间】:2019-05-09 12:26:16 【问题描述】:

我正在使用 firebase@5.5.8 和 typescript@3.1.4

每当我从 firestore 创建一个文档时,我都会得到一个 firebase.firestore.DocumentReference 类型的对象,正如预期的那样

如果我调用get(options?: firebase.firestore.GetOptions|undefined),我将按预期获得firebase.firestore.DocumentSnapshot 类型的对象

如果我打电话给data(options?: firebase.firestore.DataOptions|undefined),我将得到一个 firebase.firestore.DocumentData 对象或未定义的对象,正如预期的那样

现在,在我的 manager 对象上,我知道我在向数据库写入什么,所以我可以断言无论你从我的 manager 中得到什么 DocumentData,你都会得到一个如图所示的 Client

export interface Client 
    name: string;
    website?: string;
    description?: string;
    visible: boolean;

我想为我的管理器对象创建一个接口来表达这一点。所以我试过了:

export interface FirebaseDocumentSnapshot<T> extends $firebase.firestore.DocumentSnapshot 
    data(options?: $firebase.firestore.SnapshotOptions | undefined): T|undefined


export interface FirebaseDocumentReference<T> extends $firebase.firestore.DocumentReference 
    get(options?: $firebase.firestore.GetOptions | undefined): Promise<FirebaseDocumentSnapshot<T>>

我遇到的问题在这里:

const client: Client = mapClient(args);

const result: FirebaseDocumentReference<Client> = await $db.add(client);

错误是:

[ts]

Type 'DocumentReference' is not assignable to type 'FirebaseDocumentReference<Client>'.
  Types of property 'get' are incompatible.
    Type '(options?: GetOptions | undefined) => Promise<DocumentSnapshot>' is not assignable to type '(options?: GetOptions | undefined) => Promise<FirebaseDocumentSnapshot<Client>>'.
      Type 'Promise<DocumentSnapshot>' is not assignable to type 'Promise<FirebaseDocumentSnapshot<Client>>'.
        Type 'DocumentSnapshot' is not assignable to type 'FirebaseDocumentSnapshot<Client>'.
          Types of property 'data' are incompatible.
            Type '(options?: SnapshotOptions | undefined) => DocumentData | undefined' is not assignable to type '(options?: SnapshotOptions | undefined) => Client | undefined'.
              Type 'DocumentData | undefined' is not assignable to type 'Client | undefined'.
                Type 'DocumentData' is not assignable to type 'Client'.
                  Property 'name' is missing in type 'DocumentData'. [2322]
const result: FirebaseDocumentReference<Client>

如何声明接口以便知道结果对象的类型?

【问题讨论】:

为什么不直接转换 data() 返回的对象呢?例如:const client = snapshot.data() as Client? 因为我想返回 DocumentReference 并访问所有的 firebase 好东西,而不是返回原始数据对象 我想我不明白你为什么需要一个特殊的泛型类型 DocumentReference。 好吧。我有一个管理器服务,它控制我的应用程序对 firebase 的访问,特别是在创建新记录时,我希望从该管理器服务中返回它创建的任何实体的 DocumentReference。并且使用 TypeScript,我希望能够知道一旦获得该文档引用的数据,我将获得什么类型的数据。此外,DocumentData 有什么用?为什么 DocumentSnapshot.get() 只返回任何|未定义?或者只是任何事情 没有看到你的整个系统,真的很难理解为什么这比仅仅转换到一个界面更有帮助。您的代码将不得不对该文档中的内容做出假设,并且演员表与做出该假设的任何方式一样好。 【参考方案1】:

一个简单的解决方案是转换返回数据:

const client = snapshot.data() as Client

【讨论】:

【参考方案2】:

@gal-bracha 的解决方案将“工作”,因为 TypeScript 编译器将假定 client 的类型为 Client,但如果错误数据最终出现在 Firestore 中,它并不能防止运行时错误。在处理应用程序外部的任何数据时,更安全的解决方案是使用 Yup 之类的东西来明确地验证数据:

import * as yup from "yup";

const clientSchema = yup.object(
  name: yup.string(),
  website: yup.string().url().notRequired(),
  description: yup.string().notRequired(),
  visible: yup.boolean()
);
// You can use this elsewhere in your app
export type Client = yup.InferType<typeof clientSchema>;

// Usage
const client = await clientSchema.validate(snapshot.data());

这更具防御性,因为如果由于某种原因,不良数据最终进入 Firestore,clientSchema.validate 将引发错误。在validate() 之后,您可以保证clientClient,而不仅仅是告诉TypeScript 编译器“将其视为Client,即使在运行时也可能不是”。

【讨论】:

以上是关于如何自定义firebase DocumentData 接口?的主要内容,如果未能解决你的问题,请参考以下文章

如何自定义firebase DocumentData 接口?

如何将自定义参数与保留事件一起发送到 Firebase 分析

如何从 Android 中的 Firebase 应用内消息中检索自定义数据

Firebase 大查询 - 如何从自定义事件表中检索数据

(Swift 4) 如何在 firebase 中生成自定义自动 id?

如何使用 Firebase 云功能发送自定义电子邮件