vue.js 如何使用TypeScript接口键入useFirestore()?

t5zmwmid  于 2022-11-25  发布在  Vue.js
关注(0)|答案(1)|浏览(138)

我使用的是www.example.com提供的useFirestore() Package 器VueUse.org
但是,当使用泛型键入useFirestore()时,我不能使用具有可选属性的接口,因为TypeScript会引发以下错误:
没有与此调用匹配的重载
在下面的示例中,User接口具有一个必需的name属性,该属性会导致typescript引发错误。

interface User {
  id?: string;
  name: string; // <-- When this property is required the error will show, changing it to optional removes the error
}

const userQuery = doc(db, 'users', 'my-user-id');
const userData = useFirestore<User>(userQuery); // <-- The error shows here on "userQuery"

我找到了一个变通办法,但效果不太好。
您可以使用useFirestore(userQuery) as Ref<User>来移 debugging 误并赋予它正确的类型。
但这样做并不是一个好的解决方案,因为我基本上是在告诉编译器“我知道得更清楚”,而不是编译器在说什么。因此,在不求助于这种解决方案的情况下,拥有正确的类型会更好。

interface User {
  id?: string;
  name: string;
}

const userQuery = doc(db, 'users', 'my-user-id');
const userData = useFirestore(userQuery) as Ref<User>; // <-- See changes made here

有没有更好的方法?

xfb7svmp

xfb7svmp1#

想通了。
首先,错误的原因是userQuery被键入为DocumentReference<DocumentData>,但useFirestore<User>(userQuery)需要DocumentReference<User>。由于这两个类型不匹配,因此显示了错误。
要解决此问题,有两个选项:

选项1:

如原始问题所示,您可以 * 胁迫 * useFirestore()的传回值,如下所示:

const userQuery = doc(db, 'users', 'my-user-id');
const userData = useFirestore(userQuery) as Ref<User>;

虽然这是可行的,但也有一些缺点:
1.它覆盖了编译器。我基本上是告诉它“我知道返回值是不同的,但我知道得更清楚,我知道它存在,所以用我的来代替”。这可能会导致不可预见的问题。
1.如果传递集合查询,则需要将其类型化为数组(useFirestore(colQuery) as Ref<User[]>);如果传递文档引用,则需要将其类型化为不包含数组(useFirestore(docRef) as Ref<User>)。忘记这一点可能会导致其他错误。
1.当使用as时,你会说“this thing exists and it has this type”。但是由于useFirestore是异步的,有时数据并不存在,因为它仍在加载。因此它也应该有nullundefined的类型。因此,实际上最好使用useFirestore(userQuery) as Ref<User | null | undefined>,您必须记住每次都要这样做,否则您可能会在尝试访问一些尚不存在的数据时出现问题。
所有这些都是为了说明,我认为最好是正确地键入Firestore文档引用(或集合引用),然后让useFirestore()返回正确的类型,而不进行任何强制操作,因为强制操作可能会产生不可预见的问题。
这就引出了第二个选择...

选项2:

在我看来这是上级的选择。
我在this medium article中发现了如何使用Firestore withConverter()将进出Firestore的数据转换为自定义对象(即TypeScript接口和类)。
如果您更喜欢直接使用代码,那么this GIST可以满足您的所有需求。
但是这个解决方案是针对Firebase JavaScript Version 8 SDK的,我使用的是Firebase Modular JavaScript Version 9 SDK,它需要一些调整,如我的other question here所示。
简而言之,-withConverter可以用在Firestore doccollection方法的末尾,以正确地输入它们。它只需要一个converter参数。下面我们创建的是一个可重用的“通用转换器”,它接受您选择的接口。
所以你把.withConverter(converter<MyInterfaceHere>())放在collectiondoc的末尾,瞧!你就有了类型安全!
下面是完整的示例:

interface User {
  id?: string;
  name: string;
}

const converter = <T>() => ({
  toFirestore: (data: PartialWithFieldValue<T>) => data,
  fromFirestore: (snap: QueryDocumentSnapshot) => snap.data() as T,
});

const userDocRef = doc(db, 'users', 'my-user-id').withConverter(
  converter<User>()
);
const userData = useFirestore(userDocRef);

现在userData有一个Ref<User | null | undefined>类型,您可以访问它的所有User属性而不会出错。
它是Firestore,具有TypeScript提供的所有类型安全功能!

相关问题