【问题标题】:How to infer the return type of a generic callback function in TypeScript如何在 TypeScript 中推断通用回调函数的返回类型
【发布时间】:2022-01-07 16:01:40
【问题描述】:

我在推断通用回调函数的类型时遇到问题。这是我的代码:

import { FC, createElement, useState, useMemo, createContext as _context, useContext as _useContext } from 'react';

const createContext = <T>(useHook: (providerProps: T) => any) => {
  const ctx = _context({});

  /** Component with provider and initial values calling the hook passed. */
  const Provider: FC<T> = ({ children, ...props }) => {
    const value = useHook(props as T);
    return createElement(ctx.Provider, { value }, children);
  };

  /** Hook will return the context from the provider. */
  const useContext = () => _useContext(ctx) as ReturnType<typeof useHook>;

  /** Function will return the provider and the assign the `displayName` given. */
  const createProvider = (displayName: string) => {
    Provider.displayName = displayName;
    return Provider;
  };

  return {
    useContext,
    createProvider,
  };
};

/** Context with the values to keep track across the app. */
const ctx = createContext<{ _theme: 'light' | 'dark' }>(({ _theme }) => {
  const [theme, setTheme] = useState(_theme);
  return useMemo(() => ({ theme, setTheme }), [theme]);
});

/** Provider for the values of the theme of the app. */
export const AppThemeProvider = ctx.createProvider('AppThemeProvider');

const useAppTheme = () => {
  const { theme } = ctx.useContext();
}

我只是想要一种更简洁的方式来创建上下文,而无需提供提供程序和一些钩子。我遇到的问题是as ReturnType&lt;typeof useHook&gt; 行中没有推断出返回类型。它显示一切。并且我希望与 useHook 函数返回的类型相同。

在钩子 useAppTheme 中,const { theme } = ctx.useContext(); 主题类型的类型是 any,但它应该是 'light' | 'dark' 类型

有人可以帮助我吗?请。

谢谢!

【问题讨论】:

  • Please replace or supplement images of code or errors with plaintext versions. 您作为纯文本包含的代码中没有 systemTheme,我无法从图像复制到我的 IDE 中进行任何测试。
  • 您似乎明确地将useHook 的返回类型注释为any,所以这就是它的返回类型。如果您想做其他事情,也许在返回类型中具有通用性,例如this,但没有明文minimal reproducible example,它确切地说明了您在“将所有内容推断为任何内容”中的“所有内容”的意思,我不知道如果这完全解决了您的问题或仅部分解决了您的问题。 (或者可能什么都不做。)
  • 嗨@jcalz 谢谢你看它。遗憾的是,这并没有解决我的问题,因为在这种情况下,我需要在调用 createContext 函数时将返回类型声明为第二种类型,我想完全推断该 useHook 回调挂钩的返回值并分配它到下面类型的返回值。我尝试返回未知或从不返回,但它也不起作用。我将图像更改为文本,以便您可以轻松复制/粘贴它
  • this 对你有用吗?你必须有一个泛型类型参数,比如R;没有办法以其他方式推断。如果您有两个类型参数并且需要指定一个并推断另一个,那么您必须解决 TS 在该领域的限制(请参阅stackoverflow.com/q/57589098/2887218),例如使用柯里化。
  • @jcalz 你是世界上最好的,我不喜欢()(props =&gt; ...) 来调用函数,但这是一个解决方案!谢谢,如果我看到任何我能做的事情,我会看看柯里化。再次感谢!

标签: typescript typescript-generics


【解决方案1】:

在您的版本中,您将useHook 参数的返回类型显式注释为any,这就是编译器所看到的。如果您希望能够根据作为useHook 传入的实际回调值的返回类型来缩小范围,那么您还需要在该类型中设置createContext generic,例如:

const createContext = <T, R>(useHook: (providerProps: T) => R) => ...

然后在实现中使用R 而不是ReturnType&lt;typeof useHook&gt;。


但您不想手动指定T 和R。您想手动指定T,但让编译器推断R,不幸的是,TypeScript 目前不支持此功能。编译器将尝试推断所有类型参数,或者您必须手动指定所有这些参数。没有microsoft/TypeScript#26242 中要求的“部分类型参数推断”。所以你需要解决它。

我能想到两种解决方法。


第一种是让编译器通过手动annotating the callback parameter来推断T和R,而不是直接指定类型参数:

const ctx = createContext(({ _theme }: { _theme: 'light' | 'dark' }) => {
  const [theme, setTheme] = useState(_theme);
  return useMemo(() => ({ theme, setTheme }), [theme]);
});

这对于createContext 的调用者来说或多或少是相同的工作量,但现在编译器可以对T 和R 使用类型推断并且一切正常:

ctx.useContext().theme // "light" | "dark"

我喜欢这种方法,因为它相当自然,但如果您真的希望有人写createContext&lt;{ _theme: 'light' | 'dark' }&gt;(...),请继续阅读。


另一种解决方法是使用currying,这样您就可以使用一个类型参数的函数来返回一个类型参数的另一个函数,而不是两个类型参数的函数:

const createContext = <T>() => <R>(useHook: (providerProps: T) => R) => ...

现在您有了一个可以手动指定T 的函数和另一个编译器可以推断R 的函数:

const ctx = createContext<{ _theme: 'light' | 'dark' }>()(({ _theme }) => {
  const [theme, setTheme] = useState(_theme);
  return useMemo(() => ({ theme, setTheme }), [theme]);
});

ctx.useContext().theme // "light" | "dark"

这样做的缺点是奇怪的构造 foo&lt;T&gt;()(x, y, z) 而不是 foo&lt;T&gt;(x, y, z)。但它确实运作良好。您可以自行决定哪种解决方法最适合您的需求。

Playground link to code

【讨论】:

    猜你喜欢
    • 2019-09-03
    • 1970-01-01
    • 2021-11-08
    • 2021-04-09
    • 1970-01-01
    • 2020-01-23
    • 2020-10-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多