如何自定义 Firebase DocumentData 接口?

Sim*_*mon 9 node.js firebase typescript google-cloud-firestore

我正在使用 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 对象或未定义,正如预期的那样

现在,在我的管理器对象上,我知道我要写入数据库的内容,因此我可以断言,无论您从我的管理器中获得什么 DocumentData,您都将获得一个客户端,如图所示

export interface Client {
    name: string;
    website?: string;
    description?: string;
    visible: boolean;
}
Run Code Online (Sandbox Code Playgroud)

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

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>>
}
Run Code Online (Sandbox Code Playgroud)

我遇到的问题在这里:

const client: Client = mapClient(args);

const result: FirebaseDocumentReference<Client> = await $db.add(client);
Run Code Online (Sandbox Code Playgroud)

错误是:

[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>
Run Code Online (Sandbox Code Playgroud)

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

Gal*_*cha 16

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

const client = snapshot.data() as Client


Mik*_*sky 7

@gal-bracha 的解决方案将在 TypeScript 编译器假定client为 type的意义上“起作用” 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());
Run Code Online (Sandbox Code Playgroud)

这更具防御性,因为clientSchema.validate如果出于某种原因,Firestore 中的错误数据最终会引发错误。之后validate(),你保证,这client是一个Client,而不仅仅是告诉打字稿编译“将此视为Client即使在运行时,它可能不是”。