英文:
React hooks with typescript
问题
我有这个钩子:
import { userDB } from 'app/services/DBFunctions';
import { auth } from 'Fire';
import { collection, query, limit as fLimit, onSnapshot } from 'firebase/firestore';
import { IInvoice, IScheduledEmail, IScheduledInvoice, ITableData } from 'Interface';
import React, { useEffect, useState } from 'react';
interface Props {
collectionPath: 'scheduled-invoices' | 'scheduled-emails'
}
const useScheduledFunctions = (props: Props): IScheduledInvoice[] | IScheduledEmail[] => {
const { collectionPath } = props
const [scheduledFunctions, setScheduledFunctions] = useState<IScheduledInvoice[] | IScheduledEmail[]>([])
const user = auth.currentUser
useEffect(() => {
let queryCollection = query(
collection(userDB(), collectionPath),
)
onSnapshot(queryCollection, (snap: any) => {
let scheduledFunctions = snap.docs.map((doc: any) => doc.data())
setScheduledFunctions(scheduledFunctions)
})
}, [user, collectionPath])
return scheduledFunctions
};
export default useScheduledFunctions;
但是当我在组件中调用它时,类型要么是IScheduledInvoice,要么是IScheduledEmail,有没有一种方法可以根据集合路径推断挂钩的类型?
目前,为了避免这种冲突,我这样做:
const scheduledEmails = useScheduledFunctions({collectionPath: 'scheduled-emails'}) as IScheduledEmail[]
但我不喜欢这样做,而且这似乎不是一个好的解决方案,因为我正在覆盖类型。
英文:
I have this hook:
import { userDB } from 'app/services/DBFunctions';
import { auth } from 'Fire';
import { collection, query, limit as fLimit, onSnapshot } from 'firebase/firestore';
import { IInvoice, IScheduledEmail, IScheduledInvoice, ITableData } from 'Interface';
import React, { useEffect, useState } from 'react';
interface Props {
collectionPath: 'scheduled-invoices' | 'scheduled-emails'
}
const useScheduledFunctions = (props: Props) : IScheduledInvoice[] | IScheduledEmail[] => {
const {collectionPath} = props
const [scheduledFunctions, setScheduledFunctions] = useState<IScheduledInvoice[] | IScheduledEmail[]>([])
const user = auth.currentUser
useEffect(()=> {
let queryCollection = query(
collection(userDB(), collectionPath),
)
onSnapshot(queryCollection, (snap: any)=> {
let scheduledFunctions = snap.docs.map((doc: any)=> doc.data())
setScheduledFunctions(scheduledFunctions)
})
}, [user, collectionPath])
return scheduledFunctions
};
export default useScheduledFunctions;
But when I call it in a component, the type is either IScheduledInvoice or IScheduledEmail, is there a way to infer the type of the hook based on the collection path?
Currently when I call it, to avoid this conflict, I do this:
const scheduledEmails = useScheduledFunctions({collectionPath: 'scheduled-emails'}) as IScheduledEmail[]
But I dont like doing this and does not seem like a good solution as I am overriding the type.
答案1
得分: 1
你可以使用重载来将特定的参数类型与返回类型配对,然后提供一个接受和返回这两者的实现。
interface Props {
collectionPath: 'scheduled-invoices' | 'scheduled-emails'
}
function useScheduledFunctions(props: { collectionPath: 'scheduled-invoices' }): IScheduledInvoice[]
function useScheduledFunctions(props: { collectionPath: 'scheduled-emails' }): IScheduledEmail[]
function useScheduledFunctions(props: Props): IScheduledInvoice[] | IScheduledEmail[] {
return [] // 这里是具体实现
}
英文:
You can use overloads to pair certain argument types with return types, then provide an implementation that accepts and returns both.
interface Props {
collectionPath: 'scheduled-invoices' | 'scheduled-emails'
}
function useScheduledFunctions(props: { collectionPath: 'scheduled-invoices' }): IScheduledInvoice[]
function useScheduledFunctions(props: { collectionPath: 'scheduled-emails' }): IScheduledEmail[]
function useScheduledFunctions(props: Props): IScheduledInvoice[] | IScheduledEmail[] {
return [] // implementation here
}
答案2
得分: 1
以下是翻译好的代码部分:
interface Props {
collectionPath: 'scheduled-invoices' | 'scheduled-emails'
}
interface IScheduledInvoice {}
interface IScheduledEmail {}
type Return<T> = T extends { collectionPath: 'scheduled-emails' } ? IScheduledEmail[] : IScheduledInvoice[];
const useScheduledFunctions = <T extends Props>(props: T): Return<T> => {
return []
};
const a = useScheduledFunctions({ collectionPath: 'scheduled-emails' });
const b = useScheduledFunctions({ collectionPath: 'scheduled-invoices' });
希望这有所帮助。
英文:
There you go: Playground
interface Props {
collectionPath: 'scheduled-invoices' | 'scheduled-emails'
}
interface IScheduledInvoice {}
interface IScheduledEmail {}
type Return<T> = T extends { collectionPath: 'scheduled-emails' } ? IScheduledEmail[] : IScheduledInvoice[];
const useScheduledFunctions = <T extends Props>(props: T): Return<T> => {
return []
};
const a = useScheduledFunctions({ collectionPath: 'scheduled-emails' });
const b = useScheduledFunctions({ collectionPath: 'scheduled-invoices' });
The pro with that solution is that you can add whatever properties in the interface Props
. With overloads you will have to implement each possible arguments.
Personal opinion: if you have more than two values for collectionPath
I would go with overloads because you all have to nest ternary extends.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论