默认道具类型通过更高阶的组件

问题描述 投票:1回答:1

通过HOC传递组件会导致defaultProps信息丢失到typescript编译器。例如

themed.tsx

export interface ThemedProps {
    theme: {};
}

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export type Subtract<T extends K, K> = Omit<T, keyof K>;

const themed = <P extends ThemedProps = ThemedProps>(
    ComponentToWrap: React.ComponentType<P>
) => {
    return class ThemeWrappedComponent extends React.Component<
         Subtract<P, ThemedProps>
    > {
        static displayName = `themed(${ComponentToWrap.displayName})`;

        theme = () => {
            return {}
        };

        render() {
            return (
                <ComponentToWrap
                    {...this.props as P}
                    theme={this.theme()}
                />
            );
        }
    }
};

Foo.tsx

interface FooProps {
    theme: object,
    name: string,
}

class Foo extends React.Component<FooProps> {
    static defaultProps = {
        name: 'world'
    }
    render() {
        return <span>hello ${this.props.name}</span>
    }
}

export default themed(Foo);

当我实例化<Foo />时,我得到一个编译错误,说Property 'name' is missing in type '{}' but required in type 'Readonly<Pick<FooProps, "name">>'.

我知道有一种方法可以使用JSX.LibraryManagedAttributes来解决这类问题,但我不知道如何,而且我找不到任何关于该功能的文档。

javascript reactjs typescript higher-order-components
1个回答
2
投票

您必须利用JSX.LibraryManagedAttributes才能从您的HOC中的包装组件中提取必需和可选(默认)道具。这看起来有些棘手:

import React from 'react';

interface FooProps {
  theme: string;
  name: string;
}

class Foo extends React.Component<FooProps> {
  static defaultProps = {
    name: 'world',
  };
  render() {
    return <span>hello ${this.props.name}</span>;
  }
}

interface ThemedProps {
  theme: string;
}

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type Subtract<T extends K, K> = Omit<T, keyof K>;


const themed = <
  C extends React.ComponentType<React.ComponentProps<C> & ThemedProps>,
  // that's where all the magic happens
  ResolvedProps = JSX.LibraryManagedAttributes<C, Subtract<React.ComponentProps<C>, ThemedProps>>
>(
  Component: C
) => {
  return class ThemeWrappedComponent extends React.Component<ResolvedProps> {
    static displayName = `themed(${ComponentToWrap.displayName})`;

    render() {
      return (
        <Component
          // proper typecast since ts has fixed type infering on object rest
          {...this.props as JSX.LibraryManagedAttributes<C, React.ComponentProps<C>>} 
          theme="theme" 
        />
      );
    }
  };
};

const Wrapped = themed(Foo);

const el = <Wrapped />; // works
© www.soinside.com 2019 - 2024. All rights reserved.