React hooks with typescript

huangapple go评论51阅读模式
英文:

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 &#39;app/services/DBFunctions&#39;;
import { auth } from &#39;Fire&#39;;
import { collection, query, limit as fLimit, onSnapshot } from &#39;firebase/firestore&#39;;
import { IInvoice, IScheduledEmail, IScheduledInvoice, ITableData } from &#39;Interface&#39;;
import React, { useEffect, useState } from &#39;react&#39;;

interface Props {
    collectionPath: &#39;scheduled-invoices&#39; | &#39;scheduled-emails&#39;
}

const useScheduledFunctions = (props: Props) : IScheduledInvoice[] | IScheduledEmail[] =&gt; {
    const {collectionPath} = props
    const [scheduledFunctions, setScheduledFunctions] = useState&lt;IScheduledInvoice[] | IScheduledEmail[]&gt;([])
    const user = auth.currentUser

    useEffect(()=&gt; {
        let queryCollection = query(
            collection(userDB(), collectionPath),
        )
        onSnapshot(queryCollection, (snap: any)=&gt; {
            let scheduledFunctions = snap.docs.map((doc: any)=&gt; 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: &#39;scheduled-emails&#39;}) 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: &#39;scheduled-invoices&#39; | &#39;scheduled-emails&#39;
}

function useScheduledFunctions(props: { collectionPath: &#39;scheduled-invoices&#39; }): IScheduledInvoice[]
function useScheduledFunctions(props: { collectionPath: &#39;scheduled-emails&#39; }): IScheduledEmail[]
function useScheduledFunctions(props: Props): IScheduledInvoice[] | IScheduledEmail[] {
  return [] // implementation here
}

See Playground

答案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: &#39;scheduled-invoices&#39; | &#39;scheduled-emails&#39;
}

interface IScheduledInvoice {}

interface IScheduledEmail {}

type Return&lt;T&gt; = T extends { collectionPath: &#39;scheduled-emails&#39; } ? IScheduledEmail[] : IScheduledInvoice[];

const useScheduledFunctions = &lt;T extends Props&gt;(props: T): Return&lt;T&gt; =&gt; {
  return []
};

const a = useScheduledFunctions({ collectionPath: &#39;scheduled-emails&#39; });
const b = useScheduledFunctions({ collectionPath: &#39;scheduled-invoices&#39; });

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.

huangapple
  • 本文由 发表于 2023年2月24日 01:59:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75548606.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定