React Typescript:排除高阶组件中的属性

时间:2019-06-11 13:13:45

标签: reactjs typescript

我有一个HOC,它为包装的组件提供一个属性。

我想编写Typescript类型定义,以正确公开此HOC创建的组件的Props类型:基本上复制(推断)包装组件的props类型,并删除我的HOC提供的属性。有可能吗?

在下面的示例中,我有一个HOC withBar,它为接受该包装的任何组件提供了bar:string道具。因此,Foo = withBar(FooBar)组件应具有FooBarProps除外的道具bar,这意味着仅{foo: string}。但是Typescript显然仍然认为Foo组件也应该具有foo属性。

我使用打字稿3.3.1

import * as React from "react";

interface FooBarProps {
	foo: string;
	bar: string;
}

class FooBar extends React.PureComponent<FooBarProps> {
	public render() {
		return <>{this.props.foo} {this.props.bar}</>;
	}
}

export function withBar<TProps>(
    WrappedComponent: React.ComponentType<TProps & {bar: string}>
) {
	return class WithBar extends React.PureComponent<TProps> {

		public render() {
			return <WrappedComponent
				{...this.props}
				bar={"BAR"}
			/>;
		}
	};
}

const Foo = withBar(FooBar);

// Typescript complains here:
// Error:(29, 14) TS2741: Property 'bar' is missing in type
// '{ foo: string; }' but required in type 'Readonly<FooBarProps>'.
const foo = <Foo foo="FOO" />;

3 个答案:

答案 0 :(得分:1)

关于以下内容,它可以工作,但可能无法完全回答您的问题。 https://codesandbox.io/embed/peaceful-star-194g5?fontsize=14

其要点是将Foo和Bar分成不同的类型,并将它们结合在一起以组成包装组件。如果您不想访问沙盒,请使用以下代码:

import * as React from "react";

interface BarProps {
  bar: string;
}

interface FooProps {
  foo: string;
}

type FooBarProps = FooProps & BarProps;

class FooBar extends React.PureComponent<FooBarProps> {
  public render() {
    return (
      <>
        {this.props.foo} {this.props.bar}
      </>
    );
  }
}

export function withBar<TProps>(
  WrappedComponent: React.ComponentType<TProps & BarProps>
) {
  return class WithBar extends React.PureComponent<TProps> {
    public render() {
      return <WrappedComponent {...this.props} bar={"BAR"} />;
    }
  };
}

const Foo = withBar(FooBar);

// Typescript complains here:
// Error:(29, 14) TS2741: Property 'bar' is missing in type
// '{ foo: string; }' but required in type 'Readonly<FooBarProps>'.
export const Foof = () => <Foo foo="FOO" />;

答案 1 :(得分:0)

PickExclude类型可以在这里提供帮助:

interface Foo{
    woo: string
    doo: number
}
interface Goo{
    doo: number
}


type FooExcludeGoo = Pick<Foo, Exclude<keyof Foo, keyof Goo>> // {woo:string}

答案 2 :(得分:0)

我终于找到的最佳解决方案:

export function withBar<TProps extends {bar: string}>(
    WrappedComponent: React.ComponentType<TProps>
) {
    return class WithBar extends React.PureComponent<
        Pick<TProps, Exclude<keyof TProps, "bar">> // = Omit<TProps, "bar">
    > {
        public render() {
            return <WrappedComponent
                {...this.props as any /* still can't do better than using any here */}
                bar={"BAR"}
            />;
        }
    };
}

这个想法是,通用参数TProps包含我要从结果组件中排除的属性,并在结果组件的类型签名中手动排除它们。

唯一的警告是Typescript无法确定传递给包装组件的{...this.props}是否具有正确的类型,因此我不得不使用any,但幸运的是,它仅影响组件的内部结构,并且所有暴露在外面的类型都应该准确。

那里的Typescript错误消息也非常流行,因此我无法调试原因:TS2322: Type 'Readonly<{ children?: ReactNode; }> & Readonly<Pick<TProps, Exclude<keyof TProps, "bar">>> & { bar: string; }' is not assignable to type 'IntrinsicAttributes & TProps & { children?: ReactNode; }'. Type 'Readonly<{ children?: ReactNode; }> & Readonly<Pick<TProps, Exclude<keyof TProps, "bar">>> & { bar: string; }' is not assignable to type 'TProps'.