【问题标题】:Typescript: Exclude properties provided in a Partial打字稿:排除部分中提供的属性
【发布时间】:2020-11-19 22:27:13
【问题描述】:

我编写了一个名为withDefaults 的函数,它接受一个 React 组件(实际上是一个函数)并提供一个具有默认参数值的对象。它返回一个调用原始 React 组件(函数)并将默认参数与最终参数集合并的函数。

如果在默认集中提供了必需的参数,如何使它们不再需要?

function withDefaultProps<C extends React.ElementType>(Component: c) {
  const ResultComponent = Component as React.FunctionComponent;
  return (
    (defaultProps: Partial<React.ComponentProps<C>>) => (
      (finalProps: React.ComponentProps<C>) => (
        <ResultComponent {...defaultProps} {...finalProps}>
          {finalProps.children}
        </ResultComponent>
      )
    )
  )
}

例如,我有一个组件需要ab 作为属性。

type MyCompProps = {
  a: string,
  b: string
};

function MyComp(props: MyCompProps) {
  return null; // My react structure here
}

const MyPrefilledComp = withDefaultProps(MyComp)({ a: 'alwaysThisString' });

这是 Typescript 错误出现的地方。当我尝试使用MyPrefilledComp 时,它仍然需要我提供a 属性,即使它有一个默认值。如何调整我的 withDefaulProps 方法以使其成为可选,而不使所有属性成为可选。我已经尝试使用 keyOf 进行 Diff 和 Omit,但我的 Typescript 非常初级,所以我可能做错了。

// Typescript error: Property a is required
function MyApp() {
  return (
    <MyPrefilledComp b="a non default value" />
  )
}

我已尝试对其进行简化,并且可以轻松查看问题出在哪里,但我不确定如何解决它。类似于:Omit&lt;OriginalProps, Partial&lt;OriginalProps&gt;&gt;,但该部分会根据调用被赋予动态类型。

function withDefaults(defaultProps: Partial<OriginalProps>) {
  return function wrapper(finalProps: OriginalProps) {
    return {
      ...defaultProps,
      ...finalProps
    };
  }
}

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    我倾向于这样写你的 withDefaults() 函数:

    const makeWithDefaults = <T,>() =>
        <K extends keyof T>(defaultProps: Pick<T, K>) =>
            (finalProps: Partial<Pick<T, K>> & Omit<T, K>) =>
                ({ ...defaultProps, ...finalProps }) as T;
    
    type MyCompProps = {
        a: string,
        b: string
    };
    
    const withDefaults = makeWithDefaults<MyCompProps>();
    

    请注意,makeWithDefaults() 在运行时除了返回一个函数外并没有做太多的事情,但在编译时您可以使用泛型参数 T 来指定您尝试构建的实际对象类型。

    它返回的函数使用实用程序类型Pick&lt;T, K&gt;Partial&lt;T&gt;Omit&lt;T, K&gt;

    一旦指定TwithDefaults() 函数将接受具有来自T 的任何属性子集的defaultProps 对象,并将推断K 成为此defaultProps 对象的键。返回的函数接受Partial&lt;Pick&lt;T, K&gt;&gt; &amp; Omit&lt;T, K&gt; 类型的finalPropsOmit&lt;T, K&gt; 部分表示 finalProps 必须 具有 T 中缺少的所有属性,而 defaultProps 中缺少这些属性,Partial&lt;Pick&lt;T, K&gt;&gt; 部分表示 finalProps 可能 具有T 中包含在defaultProps 中的任何属性。而这个函数的返回类型是asserted,就是原来的T(这应该是真的,但是编译器无法验证)。


    让我们看看它是否有效:

    const hasDefaultA = withDefaults({ a: "hello" });
    
    const foo = hasDefaultA({ b: "works" });
    console.log(JSON.stringify(foo)); // {"a":"hello","b":"works"}
    const bar = hasDefaultA({}); // error! property b missing
    const baz = hasDefaultA({ a: "goodbye", b: "works" });
    console.log(JSON.stringify(baz)); // { "a": "goodbye", "b": "works" }
    

    看起来不错。我没有具体的 reactjs 经验,所以关于如何使您的 withDefaultProps() 版本正常工作,我将听从其他人的意见。

    Playground link to code

    【讨论】:

      【解决方案2】:

      我也会分享我的解决方案

      Omit 是正确的,但要实际使用它,您需要推断传递的默认道具类型

      function withDefaultProps<C extends React.ElementType>(Component: C) {
        const ResultComponent = Component as React.FunctionComponent;
      
        // we return generic function, that accepts Partial component props as argument
        return <D extends Partial<React.ComponentProps<C>>>(defaultProps: D) => (
          // now we have access to D keys and can exclude them from final props with Omit
          finalProps: Omit<React.ComponentProps<C>, keyof D> & {
            // but it needs a little workaround, because it will not have children prop otherwise
            children: React.ReactNode;
          }
        ) => (
          <ResultComponent {...defaultProps} {...finalProps}>
            {finalProps.children}
          </ResultComponent>
        );
      }
      

      使用示例: Codesandbox link

      【讨论】:

        猜你喜欢
        • 2021-10-11
        • 2019-08-15
        • 2019-04-18
        • 2019-06-30
        • 1970-01-01
        • 1970-01-01
        • 2023-02-06
        • 2019-02-25
        • 1970-01-01
        相关资源
        最近更新 更多