FirestoreDataConverter interface

Convertidor utilizado por withConverter() para transformar objetos de usuario de tipo AppModelType en datos de Firestore de tipo DbModelType .

El uso del convertidor le permite especificar argumentos de tipo genérico al almacenar y recuperar objetos de Firestore.

En este contexto, un "AppModel" es una clase que se utiliza en una aplicación para empaquetar información y funcionalidad relacionadas. Una clase de este tipo podría, por ejemplo, tener propiedades con tipos de datos anidados complejos, propiedades utilizadas para la memorización, propiedades de tipos no admitidos por Firestore (como symbol y bigint ) y funciones auxiliares que realizan operaciones compuestas. Estas clases no son adecuadas ni posibles de almacenar en una base de datos de Firestore. En cambio, las instancias de dichas clases deben convertirse en "objetos JavaScript antiguos y simples" (POJO) con propiedades exclusivamente primitivas, potencialmente anidadas dentro de otros POJO o matrices de POJO. En este contexto, este tipo se conoce como "DbModel" y sería un objeto adecuado para persistir en Firestore. Para mayor comodidad, las aplicaciones pueden implementar FirestoreDataConverter y registrar el convertidor con objetos de Firestore, como DocumentReference o Query , para convertir automáticamente AppModel a DbModel cuando se almacena en Firestore y convertir DbModel a AppModel cuando se recupera desde Firestore.

Firma:

export declare interface FirestoreDataConverter<AppModelType, DbModelType extends DocumentData = DocumentData> 

Métodos

Método Descripción
de Firestore (instantánea) Lo llama el SDK de Firestore para convertir los datos de Firestore en un objeto de tipo AppModelType . Puede acceder a sus datos llamando a: snapshot.data() . Generalmente, los datos devueltos por snapshot.data() se pueden convertir a DbModelType ; sin embargo, esto no está garantizado porque Firestore no aplica un esquema en la base de datos. Por ejemplo, las escrituras desde una versión anterior de la aplicación o las escrituras desde otro cliente que no usó un convertidor de tipos podrían haber escrito datos con diferentes propiedades y/o tipos de propiedad. La implementación deberá elegir si recuperarse correctamente de los datos no conformes o generar un error.
aFirestore(modelObjeto) Lo llama el SDK de Firestore para convertir un objeto de modelo personalizado de tipo AppModelType en un objeto Javascript simple (adecuado para escribir directamente en la base de datos de Firestore) de tipo DbModelType . Usado con setDoc() , y . El tipo WithFieldValue<T> extiende T para permitir que también se utilicen FieldValues ​​como deleteField() como valores de propiedad.
toFirestore(modelObjeto, opciones) Lo llama el SDK de Firestore para convertir un objeto de modelo personalizado de tipo AppModelType en un objeto Javascript simple (adecuado para escribir directamente en la base de datos de Firestore) de tipo DbModelType . Usado con setDoc() , y con merge:true o mergeFields . El tipo PartialWithFieldValue<T> extiende Partial<T> para permitir que FieldValues ​​como arrayUnion() se utilicen como valores de propiedad. También admite Partial anidado al permitir que se omitan campos anidados.

FirestoreDataConverter.fromFirestore()

Lo llama el SDK de Firestore para convertir los datos de Firestore en un objeto de tipo AppModelType . Puede acceder a sus datos llamando a: snapshot.data() .

Generalmente, los datos devueltos por snapshot.data() se pueden convertir a DbModelType ; sin embargo, esto no está garantizado porque Firestore no aplica un esquema en la base de datos. Por ejemplo, las escrituras desde una versión anterior de la aplicación o las escrituras desde otro cliente que no usó un convertidor de tipos podrían haber escrito datos con diferentes propiedades y/o tipos de propiedad. La implementación deberá elegir si recuperarse correctamente de los datos no conformes o generar un error.

Firma:

fromFirestore(snapshot: QueryDocumentSnapshot<DocumentData, DocumentData>): AppModelType;

Parámetros

Parámetro Tipo Descripción
instantánea ConsultaDocumentoInstantánea < DocumentoDatos , Datos del documento > Un QueryDocumentSnapshot que contiene sus datos y metadatos.

Devoluciones:

Tipo de modelo de aplicación

FirestoreDataConverter.toFirestore()

Lo llama el SDK de Firestore para convertir un objeto de modelo personalizado de tipo AppModelType en un objeto Javascript simple (adecuado para escribir directamente en la base de datos de Firestore) de tipo DbModelType . Usado con setDoc() , y .

El tipo WithFieldValue<T> extiende T para permitir que también se utilicen FieldValues ​​como deleteField() como valores de propiedad.

Firma:

toFirestore(modelObject: WithFieldValue<AppModelType>): WithFieldValue<DbModelType>;

Parámetros

Parámetro Tipo Descripción
modeloObjeto Con valor de campo <Tipo de modelo de aplicación>

Devoluciones:

Con valor de campo <TipoDeModeloDb>

FirestoreDataConverter.toFirestore()

Lo llama el SDK de Firestore para convertir un objeto de modelo personalizado de tipo AppModelType en un objeto Javascript simple (adecuado para escribir directamente en la base de datos de Firestore) de tipo DbModelType . Usado con setDoc() , y con merge:true o mergeFields .

El tipo PartialWithFieldValue<T> extiende Partial<T> para permitir que FieldValues ​​como arrayUnion() se utilicen como valores de propiedad. También admite Partial anidado al permitir que se omitan campos anidados.

Firma:

toFirestore(modelObject: PartialWithFieldValue<AppModelType>, options: SetOptions): PartialWithFieldValue<DbModelType>;

Parámetros

Parámetro Tipo Descripción
modeloObjeto ParcialConValorDeCampo <Tipo de modelo de aplicación>
opciones Establecer opciones

Devoluciones:

ParcialConValorDeCampo <TipoDeModeloDb>

Ejemplo

Ejemplo sencillo

const numberConverter = {
    toFirestore(value: WithFieldValue<number>) {
        return { value };
    },
    fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
        return snapshot.data(options).value as number;
    }
};

async function simpleDemo(db: Firestore): Promise<void> {
    const documentRef = doc(db, 'values/value123').withConverter(numberConverter);

    // converters are used with `setDoc`, `addDoc`, and `getDoc`
    await setDoc(documentRef, 42);
    const snapshot1 = await getDoc(documentRef);
    assertEqual(snapshot1.data(), 42);

    // converters are not used when writing data with `updateDoc`
    await updateDoc(documentRef, { value: 999 });
    const snapshot2 = await getDoc(documentRef);
    assertEqual(snapshot2.data(), 999);
}

Ejemplo avanzado

// The Post class is a model that is used by our application.
// This class may have properties and methods that are specific
// to our application execution, which do not need to be persisted
// to Firestore.
class Post {
    constructor(
        readonly title: string,
        readonly author: string,
        readonly lastUpdatedMillis: number
    ) {}
    toString(): string {
        return `${this.title} by ${this.author}`;
    }
}

// The PostDbModel represents how we want our posts to be stored
// in Firestore. This DbModel has different properties (`ttl`,
// `aut`, and `lut`) from the Post class we use in our application.
interface PostDbModel {
    ttl: string;
    aut: { firstName: string; lastName: string };
    lut: Timestamp;
}

// The `PostConverter` implements `FirestoreDataConverter` and specifies
// how the Firestore SDK can convert `Post` objects to `PostDbModel`
// objects and vice versa.
class PostConverter implements FirestoreDataConverter<Post, PostDbModel> {
    toFirestore(post: WithFieldValue<Post>): WithFieldValue<PostDbModel> {
        return {
            ttl: post.title,
            aut: this._autFromAuthor(post.author),
            lut: this._lutFromLastUpdatedMillis(post.lastUpdatedMillis)
        };
    }

    fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): Post {
        const data = snapshot.data(options) as PostDbModel;
        const author = `${data.aut.firstName} ${data.aut.lastName}`;
        return new Post(data.ttl, author, data.lut.toMillis());
    }

    _autFromAuthor(
        author: string | FieldValue
    ): { firstName: string; lastName: string } | FieldValue {
        if (typeof author !== 'string') {
            // `author` is a FieldValue, so just return it.
            return author;
        }
        const [firstName, lastName] = author.split(' ');
        return {firstName, lastName};
    }

    _lutFromLastUpdatedMillis(
        lastUpdatedMillis: number | FieldValue
    ): Timestamp | FieldValue {
        if (typeof lastUpdatedMillis !== 'number') {
            // `lastUpdatedMillis` must be a FieldValue, so just return it.
            return lastUpdatedMillis;
        }
        return Timestamp.fromMillis(lastUpdatedMillis);
    }
}

async function advancedDemo(db: Firestore): Promise<void> {
    // Create a `DocumentReference` with a `FirestoreDataConverter`.
    const documentRef = doc(db, 'posts/post123').withConverter(new PostConverter());

    // The `data` argument specified to `setDoc()` is type checked by the
    // TypeScript compiler to be compatible with `Post`. Since the `data`
    // argument is typed as `WithFieldValue<Post>` rather than just `Post`,
    // this allows properties of the `data` argument to also be special
    // Firestore values that perform server-side mutations, such as
    // `arrayRemove()`, `deleteField()`, and `serverTimestamp()`.
    await setDoc(documentRef, {
        title: 'My Life',
        author: 'Foo Bar',
        lastUpdatedMillis: serverTimestamp()
    });

    // The TypeScript compiler will fail to compile if the `data` argument to
    // `setDoc()` is _not_ compatible with `WithFieldValue<Post>`. This
    // type checking prevents the caller from specifying objects with incorrect
    // properties or property values.
    // @ts-expect-error "Argument of type { ttl: string; } is not assignable
    // to parameter of type WithFieldValue<Post>"
    await setDoc(documentRef, { ttl: 'The Title' });

    // When retrieving a document with `getDoc()` the `DocumentSnapshot`
    // object's `data()` method returns a `Post`, rather than a generic object,
    // which would have been returned if the `DocumentReference` did _not_ have a
    // `FirestoreDataConverter` attached to it.
    const snapshot1: DocumentSnapshot<Post> = await getDoc(documentRef);
    const post1: Post = snapshot1.data()!;
    if (post1) {
        assertEqual(post1.title, 'My Life');
        assertEqual(post1.author, 'Foo Bar');
    }

    // The `data` argument specified to `updateDoc()` is type checked by the
    // TypeScript compiler to be compatible with `PostDbModel`. Note that
    // unlike `setDoc()`, whose `data` argument must be compatible with `Post`,
    // the `data` argument to `updateDoc()` must be compatible with
    // `PostDbModel`. Similar to `setDoc()`, since the `data` argument is typed
    // as `WithFieldValue<PostDbModel>` rather than just `PostDbModel`, this
    // allows properties of the `data` argument to also be those special
    // Firestore values, like `arrayRemove()`, `deleteField()`, and
    // `serverTimestamp()`.
    await updateDoc(documentRef, {
        'aut.firstName': 'NewFirstName',
        lut: serverTimestamp()
    });

    // The TypeScript compiler will fail to compile if the `data` argument to
    // `updateDoc()` is _not_ compatible with `WithFieldValue<PostDbModel>`.
    // This type checking prevents the caller from specifying objects with
    // incorrect properties or property values.
    // @ts-expect-error "Argument of type { title: string; } is not assignable
    // to parameter of type WithFieldValue<PostDbModel>"
    await updateDoc(documentRef, { title: 'New Title' });
    const snapshot2: DocumentSnapshot<Post> = await getDoc(documentRef);
    const post2: Post = snapshot2.data()!;
    if (post2) {
        assertEqual(post2.title, 'My Life');
        assertEqual(post2.author, 'NewFirstName Bar');
    }
}