打字稿:为通用React组件定义未声明的道具

时间:2019-02-27 20:18:26

标签: javascript reactjs typescript react-native typescript-typings

我有这个简单的通用React组件,在这里我永远不会知道我将使用哪种元素类型或道具,也不想为每个潜在的道具设置接口。

使用TS v2.9。

在这种情况下,我使用React Native而不使用React的事实应该不重要。

import React, { StatelessComponent as SC } from "react";

interface IGenComponentProps {    
  /**
   * Component type
   */
  ComponentName: string;

  /**
   * Test Id used for automation testing
   */
  testId?: string;

  /**
   * I'd like to define this here to supress TS errors I'm getting
   */         
  remainderProps?: any;
}

const GenComponent:SC<IGenComponentProps> = ({ComponentName, testId, children, ...remainderProps}) => {

  return (
    <ComponentName id={testId} {...remainderProps}>
      {children}
    </ComponentName>
  )
}

export default GenComponent;

这很好并且可以预期,但是在使用这样的组件时出现TS错误:

   <GenComponent 
      ComponentName={Image}
      testId="test-image"
      source={{uri: 'someImageSrc'}}
      opacity={0.1}
    />
  

[ts]属性'source'在类型'IntrinsicAttributes&IGenComponentProps&{children ?: ReactNode; }'。

     

[ts]属性'opacity'在类型'IntrinsicAttributes&IGenComponentProps&{children ?: ReactNode; }'。

或:

   <GenComponent
      ComponentName={View}
      testId="left-container"
      accessibilityHint="left-container"
    >
      { someContent }
   </GenComponent>
  

[ts]属性'accessibilityHint'在类型'IntrinsicAttributes&IGenComponentProps&{children ?: ReactNode; }'。

2 个答案:

答案 0 :(得分:1)

如果您希望在不枚举那些名称的情况下允许使用任何名称的属性,则可以执行以下操作:

interface IGenComponentProps {    
  ComponentName: string;
  testId?: string;
  [key: string]: any;
}

请注意,您将对此进行有限的类型检查。您的IDE基本上会强制ComponentName存在并且是字符串,如果testId存在,则它是字符串。其他所有东西都是免费的,但至少是允许的。

答案 1 :(得分:0)

您可以放松组件的props接口,但是所有GenComponent都会丢失其所有子类型的信息。

相反,我将使用通用的高阶组件:

interface ITest {
  id: string;
}

function withId<T>(WrappedComponent) {
  return class extends Component<ITest & T> {
    public render() {
      return <WrappedComponent id={this.props.id} {...this.props} />;
    }
  };
}

const TestableImage = withId<ImageProps>(Image);

const ExampleUsage = props =>
  <View>
    <TestableImage
    id="required"
    source={{ uri: "also required" }}
    // Plus any other optional image image props
    />
  </View>;

在这种情况下,您包装的组件将具有props接口ITest & ImageProps

我在这种模式下找到了good article