TinyTiger
TinyTiger

Reputation: 2111

TypeScript + Vue: What return Type to use for an object that contains a reactive() and ref() variable?

I have a Vue 3 composable function.

It returns an object that contains one reactive() variable and one ref() variable.

And I would like to set a return type on this function to satisfy an eslint error.

But I'm new to Vue and TypeScript and not sure what the return type should be.

So what should I use as the useCollection() return type?

import { ref, reactive, watchEffect } from "vue";
import { projectFirestore } from "@/firebase/config";
import {
  query,
  orderBy,
  onSnapshot,
  DocumentData,
  collection,
} from "firebase/firestore";

const useCollection: any = (collectionPath: string) => { //<-- Need to replace this "any" with something else
  const documentsArray = reactive<Record<string, unknown>[]>([]);
  const error = ref<string | null>(null);
  // references
  const collectionReference = collection(projectFirestore, collectionPath);
  const collectionOrdered = query(
    collectionReference,
    orderBy("createdAt", "desc")
  );
  // updates
  const unsubscribe = onSnapshot(
    collectionOrdered,
    (snapshot) => {
      documentsArray.splice(0);
      snapshot.forEach((doc: DocumentData) => {
        documentsArray.push({ ...doc.data(), id: doc.id });
      });
      error.value = null;
    },
    (err) => {
      console.log(err.message);
      documentsArray.splice(0);
      error.value = err.message;
    }
  );
  watchEffect((onInvalidate) => {
    console.log("watchEffect fired");
    onInvalidate(() => {
      console.log("onInvalidate fired");
      unsubscribe();
    });
  });
  return { documentsArray, error };
};

export default useCollection;

Upvotes: 3

Views: 7170

Answers (2)

Estus Flask
Estus Flask

Reputation: 223104

reactive returns the same object type as provided, with inner refs being unwrapped, which can be ignored depending on specified object.

ref returns Ref type.

For this function a type would be:

(collectionPath: string) => { documentsArray: Record<string, unknown>[] , error: Ref<string | null> }

There may be no need to specify the type for useCollection variable because it's inferred from the function. Return type is inferred from the single return value, { documentsArray, error }, where both documentsArray and error are constants that have their types inferred on assignment either.

If a type needs to be more specific than inferred one, e.g. collection values are known, this is handled by generics:

const useCollection = <T extends unknown = unknown>(collectionPath: string) => {
  const documentsArray = reactive<Record<string, T>[]>([]);
  const error = ref<string | null>(null);
  ...

const c = useCollection<string | number>(...);

Upvotes: 3

ericobi
ericobi

Reputation: 549

Mine works with any but I guess it is because of your project rules like eslint or tsconfig.

You need to define your return type like this.

const useCollection: (collectionPath: string) : { documentsArray: reactive<Record<string, unknown>[]>; error: ref<string | null>; } => { 
     // write your code
}

Or you can use by defining interface.

interface CollectionResult {
    documentsArray: reactive<Record<string, unknown>[]>;
    error: ref<string | null>;
}
const useCollection: (collectionPath: string) : CollectionResult => { 
    // write your code
}

Upvotes: -1

Related Questions