【问题标题】:Can't assign specific type as an argument to a generic无法将特定类型作为参数分配给泛型
【发布时间】:2021-05-19 20:45:50
【问题描述】:

我正在尝试设置一个接口,我可以在其中传递参数而无需指定其类型。

// Wrapper.tsx
interface Props extends MenuProps, ChakraProps {
  handleTagClick: <T>(args?: T) => void
}

export const Wrapper: FC<Props> = ({ handleTagClick }): JSX.Element => {
  return(
    <Button 
       {...(
          handleTagClick && { 
            onClick: () => handleTagClick({ ...dummyQuestion, type }) })
       }
    />
  )
}

然后我调用错误出现的那个组件:

export const Container = () => {

  const { addNewThing } = useMyHook()

  // The error comes from this line
  return <Wrapper handleTagClick={addNewThing} />
}

错误:

Type '(question: VariableConfig) => void' is not assignable to type '<T>(args?: T | undefined) => void'.
  Types of parameters 'question' and 'args' are incompatible.
    Type 'T | undefined' is not assignable to type 'VariableConfig'.
      Type 'undefined' is not assignable to type 'VariableConfig'.
Wrapper.tsx(10, 3): The expected type comes from property 'handleTagClick' which is declared here on type 'IntrinsicAttributes & Partial<Props> & { children?: ReactNode; }'

我做错了什么?

【问题讨论】:

  • 错误是泛型函数类型接受undefined 作为参数类型,但您尝试传递给它的函数不接受,这是不兼容的。
  • 可能的快速修复:&lt;T&gt;(args: T) =&gt; void,因为您永远不会使用 undefined(没有任何参数)调用它。
  • @EmileBergeron 同样的错误:Type '(question: VariableConfig) =&gt; void' is not assignable to type '&lt;T&gt;(args: T) =&gt; void'. Types of parameters 'question' and 'args' are incompatible. Type 'T' is not assignable to type 'VariableConfig'.
  • 请考虑修改此处的代码以构成minimal reproducible example 适合放入像The TypeScript Playground 这样的独立IDE 中,以便其他人可以自己演示您的问题(并且只有您的问题)。这允许其他人着手解决问题,而不是首先需要重新创建问题。我在这里强烈怀疑你需要Props 是通用的,而不是handleTagClick,但是如果没有我可以玩的例子,我不想冒险猜测实际答案。
  • 我正在考虑它,然后意识到实际上没有直接的解决方案,因为它没有任何意义。在您的情况下,看起来您已经知道通用包装器中的类型(或类型的一部分),因为handleTagClick({ ...dummyQuestion, type }) 显式传递了一个参数,您可能会想出一些通用类型来扩展。否则,如果它真的是可选的并且事先未知,那么泛型就没有意义,它可能应该是handleTagClick: (args?: any) =&gt; void

标签: javascript reactjs typescript ecmascript-6


【解决方案1】:

您设计了组件,以便它在道具中接受带有unknown 类型参数的函数。这意味着,在组件内部,它假设可以为这个函数提供这样的参数。当您传递只接受 VariableConfig 类型的参数的函数时,如果它有另一个类型的参数 (unknown) 可能会导致错误。这不是预期的。要解决这个问题:

interface Props extends MenuProps, ChakraProps {
  handleTagClick: (args: VariableConfig) => void
}

UPD。由于我的回答似乎不完整,我在解释中添加了更多细节。我将它们分成几段。

  1. 首先。 TypeScript 本质上是一个框架,可让您实现类型安全。这意味着它会跟踪所有变量的所有可能(尽可能)状态,并说明其中一些是否不安全。这是当一个变量可以绑定到一个不允许的类型的值时。你在编译阶段就知道了。但如果忽略它可能会导致运行时错误。
  2. 我不知道你的应用的业务逻辑来判断你真正需要什么。但是,如果您有一个组件正在接受该函数,而该函数又接受unknown 类型的参数,然后您给出接受unknown 约束的函数,这是一个危险的情况。想象一下,其逻辑内部的组件给这个函数一个约束之外的值。这将意味着可能的运行时错误。
  3. 对我来说,它表示您在组件设计中存在缺陷。您需要审核应用内部的逻辑。
  4. 通用
interface Props extends MenuProps, ChakraProps {
  handleTagClick: <T>(args?: T) => void;
}

没有多大意义。其实这和表达是一样的

interface Props extends MenuProps, ChakraProps {
  handleTagClick: (args?: unknown) => void;
}
  1. 可能的解决方案。你可以关闭 TypeScript 正在做的工作。你可以这样做:
return <Wrapper handleTagClick={addNewThing as any} />;

playground

但在这种情况下,您会遇到不确定性,TypeScrip 将不再为您提供帮助。

  1. 第二种解决方案。写两个不同的组件。一个handleTagClick 接受所有类型的参数,第二个只接受VariableConfig
interface Props extends MenuProps, ChakraProps {
  handleTagClick: (args?: unknown) => void;
}

interface PropsVariableConfig extends MenuProps, ChakraProps {
  handleTagClick: (args: VariableConfig) => void;
}

export const Wrapper: FC<Props> = ({ handleTagClick }): JSX.Element => {

  return (
    <Button
      {...{
        onClick: () => handleTagClick({ ...dummyQuestion, type }),
      }}
    />
  );
};

export const WrapperVariableConfig: FC<PropsVariableConfig> = ({
  handleTagClick,
}): JSX.Element => {

  return (
    <Button
      {...{
        onClick: () => handleTagClick(dummyQuestion),
      }}
    />
  );
};

export const Container = () => {
  const { addNewThing } = useMyHook();

  return <WrapperVariableConfig handleTagClick={addNewThing} />;
};

playground

  1. 您可以做的最后一件事是为函数添加一个类型保护包装器。当参数的类型不受支持时,它将过滤引发错误的参数。
declare const isVariableConfigGuard = (x: unknown): x is VariableConfig;

const wrapInVariableConfigGuard = (
  f: (question: VariableConfig) => void
): ((args?: unknown) => void) => (args?: unknown) => {
  if (isVariableConfigGuard(args)) {
    return f(args);
  }

  throw Error('An argument is of not supported type');
};

export const Container = () => {
  const { addNewThing } = useMyHook();

  return <Wrapper handleTagClick={wrapInVariableConfigGuard(addNewThing)} />;
};

playground

但在这种情况下,您需要在应用的上层设置错误边界。这种模式是为了让逻辑更不透明,更混乱。

【讨论】:

  • 这不是我需要的,因为handleTagClick 可能接受其他类型作为参数,而不仅仅是VariableConfig
  • 让我展开我的解释。 1. 首先。 TypeScript 本质上是一个框架,可让您实现类型安全。这意味着它会跟踪所有变量的所有可能(尽可能)状态,并说明其中一些是否不安全。这是当一个变量可以绑定到一个不允许的类型的值时。你在编译阶段就知道了。但是如果你忽略它可能会导致运行时错误。
  • 2.我不知道您的应用程序的业务逻辑来判断您真正需要什么。但是,如果您有一个组件正在接受函数,而该函数又接受unknown 类型的参数,然后您给出接受unknown 约束的函数,这是危险的情况。想象一下,其逻辑内部的组件给这个函数一个约束之外的值。这将意味着可能的运行时错误。
  • 3.对我来说,它说你在组件的设计上有缺陷。您需要的是审核应用程序内部的逻辑。
  • 4.通用的interface Props extends MenuProps, ChakraProps { handleTagClick: &lt;T&gt;(args?: T) =&gt; void } 没有多大意义。其实这和表达interface Props extends MenuProps, ChakraProps { handleTagClick: (args?: unknown) =&gt; void }是一样的
猜你喜欢
  • 2021-04-19
  • 1970-01-01
  • 1970-01-01
  • 2023-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-18
  • 2020-06-18
相关资源
最近更新 更多