首页 > 解决方案 > 打字稿:排除部分中提供的属性

问题描述

我编写了一个名为的函数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<OriginalProps, Partial<OriginalProps>>但是根据调用为该部分赋予动态类型。

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

标签: reactjstypescript

解决方案


我倾向于这样写你的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<T, K>Partial<T>Omit<T, K>

一旦T指定,该withDefaults()函数将接受具有defaultProps来自 的任何属性子集的对象T,并将推断K为该defaultProps对象的键。返回的函数接受 afinalProps类型Partial<Pick<T, K>> & Omit<T, K>。该Omit<T, K>部分说finalProps 必须T具有中缺少的所有属性defaultProps,并且该Partial<Pick<T, K>>部分说finalProps 可能T具有中包含的任何属性defaultProps。并且这个函数的返回类型被断言为原始的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 代码链接


推荐阅读