【问题标题】:TypeScript Rest Props - allow any props or tell the component which props they're going to beTypeScript Rest Props - 允许任何道具或告诉组件它们将成为哪些道具
【发布时间】:2021-03-22 00:14:43
【问题描述】:

我正在使用 TypeScript 构建一个 React 应用程序。我有一个RequiresPermission 组件,它基于谓词,应该呈现一个或另一个组件并转发所有道具。

type Props = {
  NotPermittedComponent: React.ComponentType;
  PermittedComponent: React.ComponentType;
  isPermitted: boolean;
};

const RequiresPermisson = ({
  NotPermittedComponent,
  PermittedComponent,
  isPermitted,
  ...rest
}: Props) =>
  isPermitted ? (
    <PermittedComponent {...rest} />
  ) : (
    <NotPermittedComponent {...rest} />
  );

export default RequiresPermisson;

当我渲染组件时,TypeScript 大喊RequiresPermission in:

const PERMITTED_TEXT = 'permitted';
const NOT_PERMITTED_TEXT = 'not-permitted';

type TestPropsProps = {
  text: string;
};

const NotPermittedTestComponent: React.FunctionComponent<TestPropsProps> = ({
  text,
}) => (
  <div>
    <span>{NOT_PERMITTED_TEXT}</span>
    {text}
  </div>
);

const PermittedTestComponent: React.FunctionComponent<TestPropsProps> = ({
  text,
}) => (
  <div>
    <span>{PERMITTED_TEXT}</span>
    {text}
  </div>
);

const createProps = ({
  NotPermittedComponent = NotPermittedTestComponent,
  PermittedComponent = PermittedTestComponent,
  isPermitted = false,
  text = 'foo',
} = {}) => ({
  NotPermittedComponent,
  PermittedComponent,
  isPermitted,
  text,
});

const props = createProps();
render(<RequiresPermission {...props} />);

说:

Type '{ NotPermittedComponent: FunctionComponent<TestPropsProps>; PermittedComponent: FunctionComponent<TestPropsProps>; isPermitted: boolean; text: string; }' is not assignable to type '{ NotPermittedComponent: ComponentType<{}>; PermittedComponent: ComponentType<{}>; isPermitted: boolean; }'.
  Types of property 'NotPermittedComponent' are incompatible.
    Type 'FunctionComponent<TestPropsProps>' is not assignable to type 'ComponentType<{}>'.
      Type 'FunctionComponent<TestPropsProps>' is not assignable to type 'FunctionComponent<{}>'.
        Types of parameters 'props' and 'props' are incompatible.
          Type '{ children?: ReactNode; }' is not assignable to type 'PropsWithChildren<TestPropsProps>'.
            Property 'text' is missing in type '{ children?: ReactNode; }' but required in type 'TestPropsProps'.

我也尝试将Record&lt;string, unknown&gt; 用作props,但这也不起作用。

您如何解决此问题以传递道具或允许任何(不是any 类型)道具,以便...rest 参数起作用?

【问题讨论】:

  • “允许任何道具” - (props: any) =&gt; ...? unknown 作为 return 类型比作为参数类型更有意义。
  • 由于您不接受任何答案,我会假设缺少某些内容。您能否就您的问题的哪些部分没有得到回答或为什么它不适合您的用例提供任何反馈?

标签: reactjs typescript typescript-typings react-typescript


【解决方案1】:

不确定这是否是最优雅的解决方案,但我刚刚组合了所有道具类型。这使您可以对所有道具进行类型检查。我必须转发所有道具,因为我无法确保子组件不需要像 isPermitted 这样的东西。

interface Props <A, B>{
  NotPermittedComponent: React.ComponentType<A>;
  PermittedComponent: React.ComponentType<B>;
  isPermitted: boolean;
}

const RequiresPermisson = <A, B>(props: Props<A, B> & A & B): JSX.Element => {
  const {
    NotPermittedComponent,
    PermittedComponent,
    isPermitted,
  } = props
  return isPermitted ? (
    <PermittedComponent {...props} />
  ) : (
    <NotPermittedComponent {...props} />
  )
}

【讨论】:

  • 这个太棒了!为什么你需要&lt;A, B&gt; 而不仅仅是&lt;A&gt;? @ian
  • A 和 B 只是 PermittedComponent 和 NotPermittedComponent 的 props,因为要求它们不同,所以我们需要它们的不同类型。 A 和 B 将从您插入的组件中推断出来,并且在 RequiersPermission 上将需要/可能需要所有这些组件。 @Gabriel
  • 我接受了答案,因为至少它可以编译,但从技术上讲还有另一个问题。 PermittedComponent 和 NotPermittedComponent 现在将自己和 isPermitted 作为道具传递给它们,但这些不是它们的道具。你有什么解决办法吗?
  • 我理解你的担心。但是即使使用代码中的...rest,您也会给PermittedComponent 以及NotPermittedComponent 的道具。如果我写一个PermittedComponent 有isPermitted 作为道具怎么办。我不确定有没有一种方法可以将所有这些分开而不将道具隔离成permittedComponentProps 之类的东西或将它们作为renderProps 传递。
【解决方案2】:

您需要来自 typescript 的泛型类型:https://www.typescriptlang.org/docs/handbook/generics.html

简而言之,这些是取决于输入内容的动态类型。

在下面的代码中,我们分配了所有传递给类型 T 的 props,并告诉 typescript 这些 props 是: required props NotPermittedComponent PermittedComponent isPermitted 以及所有的 rest,在这种情况下变成了类型 T。

type Props = {
  NotPermittedComponent: React.ComponentType;
  PermittedComponent: React.ComponentType;
  isPermitted: boolean;
};

const RequiresPermisson = <T extends Record<string, unknown>>({
  NotPermittedComponent,
  PermittedComponent,
  isPermitted,
  ...rest
}: Props & T) =>
  isPermitted ? (
    <PermittedComponent {...rest} />
  ) : (
    <NotPermittedComponent {...rest} />
  );

export default RequiresPermisson;

【讨论】:

  • 感谢您的帮助。我试过你的代码,它抛出了同样的错误:Types of property 'NotPermittedComponent' are incompatible. Type 'FunctionComponent&lt;TestProps&gt;' is not assignable to type 'ComponentType&lt;{}&gt;'. Type 'FunctionComponent&lt;TestProps&gt;' is not assignable to type 'FunctionComponent&lt;{}&gt;'. Types of parameters 'props' and 'props' are incompatible. Type '{ children?: ReactNode; }' is not assignable to type 'PropsWithChildren&lt;TestProps&gt;'. Property 'text' is missing in type '{ children?: ReactNode; }' but required in type 'TestProps'.
  • 天哪,这是复杂的东西。一直在尝试构建一种将任何组件作为PermittedComponent 和NotPermittedComponent 传递的方法,并且需要传递RequiresPermisson 组件上的所有且仅道具。一直在寻找互联网,似乎不是一种方式 AFAIK。问题是 PermittedComponent 和 NotPermittedComponent 的动态道具,这些总是只包含 {text: string} 吗?在那种情况下,我会很简单。
  • 我知道是对的 ? 感谢您在周末帮助我顺便说一句,真的很感激?我也花了几个小时在这上面,但找不到方法来完成这项工作。不,PermittedComponent 和 NotPermittedComponent 甚至可以包含两个不同的道具(所以一个{ myNumber: number } 和另一个{ myBool: boolean })。
  • type PropsOf&lt;T&gt; = T extends React.ComponentType&lt;infer Props&gt; ? Props : never 我看到它在某处用于获取传递组件的道具,我尝试了const RequiresPermisson = &lt; P extends Record&lt;"PermittedComponent" | "NotPermittedComponent", unknown&gt; &gt;({ 之类的东西,希望这可以让我添加PropsOf&lt;P["PermittedComponent"]&gt; 作为必需的道具。可以让它动态工作:/
猜你喜欢
  • 2021-05-22
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 2018-08-22
  • 1970-01-01
  • 2017-11-05
  • 2022-10-13
  • 1970-01-01
相关资源
最近更新 更多