如何在导入的反应组件上转换类型?

How to cast type on imported react component?

我希望能够做这样的事情:

import Form as React.Component<IFormProps> from './Form';

这样我就可以使用该组件,它需要 IFormProps 接口中定义的道具。

我正在尝试这样做,因为我的表单组件使用 redux-formredux,而这两个装饰器对我来说根本不起作用。我花了太多时间在谷歌上搜索有关如何执行此操作的示例,但没有任何效果。这是我的表单导出的样子,因为其他都不起作用。

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)((reduxForm as any)(formConfig)(Form)) as any;

真的不应该这么难,我的 Form 组件中有一个 IFormProps 接口,我想要的只是让打字稿识别其中所需的道具。

编辑: 根据要求,我添加了更多关于我的组件外观的信息,但请注意,就打字而言,该组件相当复杂。我在让这些 connect 和 redux-form 装饰器工作时遇到了很多麻烦,我不得不做很多变通办法。而且浪费了很多时间。归根结底,我只需要组件根据 IFormProps 进行验证,我什至不关心装饰器是否不能一起工作。在这里或 google 在这方面似乎没有太多帮助。无论如何,下面有更多代码:

Form.tsx

import * as React from 'react';
import {
  clearForm,
  doFormSubmit,
  getFormRelatedValues,
  IClearForm,
  IDoFormSubmit,
  IGetFormRelatedValues,
  ISearchFormRelatedValues,
  IUploadFile,
  searchFormRelatedValues,
  uploadFile,
} from '../actions/formActions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { reduxForm, SubmissionError } from 'redux-form';

const { Component } = React;

function onSubmitFail(errors: IDCRA.IGenericObj,
  dispatch: IDCRA.IDispatch,
  submitError: IDCRA.IGenericObj,
  props: IFromProps) {
  // Something....
}

function scrollToFirstError() {
  // Something else....
}

const formConfig = {
  onSubmitFail: (
    errors: IDCRA.IGenericObj,
    dispatch: IDCRA.IDispatch,
    submitError: IDCRA.IGenericObj,
    props: IFromProps,
  ) => scrollToFirstError(errors, props),
  returnRejectedSubmitPromise: true,
  validate: someValidate,
};

declare interface IFromProps {
  // setFormWarningMessage?: (msg: string) => void;
  appContext?: string;
  asyncBlurFields?: string[];
  asyncValidate?: IAsyncValidate;
  change?: IDCRA.IChangeFieldValue;
  clearFormConnect?: IClearForm;
  doFormSubmitConnect?: IDoFormSubmit;
  error?: string;
  fields: { [key: string]: IDCRA.IField };
  firstPage: boolean;
  form: string;
  formObj: IDCRA.IForm;
  formPageIdentifier: string;
  getFormRelatedValuesConnect?: IGetFormRelatedValues;
  goToPrevPage?: () => any;
  handleSubmit?: (fn: (values: IDCRA.IGenericObj, dispatch: IDCRA.IDispatch) => any) => any;
  i18n?: IDCRA.IGenericObj;
  initialValues?: IDCRA.IGenericObj;
  invalid?: boolean;
  isCreateMode: boolean;
  lang: string;
  lastPage: boolean;
  onSubmitSuccess: (response: IDCRA.ISaveCardResponseObj) => any;
  ownerIdentifier: string;
  partialSave?: boolean;
  pristine?: boolean;
  rows: string[][];
  searchFormRelatedValuesConnect?: ISearchFormRelatedValues;
  submitButtonLabel?: string;
  submitFailed?: boolean;
  submitting?: boolean;
  untouch?: IDCRA.IUntouchField;
  uploadFileConnect?: IUploadFile;
  waitForEvent?: boolean;
}

class Form extends Component<IFromProps, {}> {
  constructor(props: IFromProps) {
    super(props);

    this.handleFormSubmit = this.handleFormSubmit.bind(this);
  }

  handleFormSubmit(values: IDCRA.IGenericObj, dispatch: IDCRA.IDispatch) {
    // Handles form submit....
  }

  render() {
    // Props are consumed here and used to build the form
    const {handleSubmit, identifier} = this.props;

    return (
      <div className="form" id={`form-container-${identifier}`}>
        <form onSubmit={handleSubmit(this.handleFormSubmit)}>
          <div className="card bg-default">

            {/* my form parts are here, not important */}

          </div>
        </form>
      </div>
    );
  }
}

function mapStateToProps(state: IDCRA.IAppState) {
  return {
    appContext: state.appCoreData.appCoreData.appContext,
    i18n: state.appCoreData.appCoreData.i18n,
  };
}

// I have to use my own dispatch type because by default I get errors...
// It's really hard to debug these deply nested TS errors, the messages are cryptic and could be coming from multple source
function mapDispatchToProps(dispatch: IDCRA.IDispatch) {
  return bindActionCreators(
    {
      // setFormWarningMessage,
      clearFormConnect: clearForm,
      doFormSubmitConnect: doFormSubmit,
      getFormRelatedValuesConnect: getFormRelatedValues,
      searchFormRelatedValuesConnect: searchFormRelatedValues,
      uploadFileConnect: uploadFile,
    },
    dispatch,
  );
}

//  Decorate the form component
export default connect(
  mapStateToProps,
  mapDispatchToProps,
)((reduxForm as any)(formConfig)(Form)) as any;

如果我从连接的语句中删除我的 any 转换,这就是我得到的错误:

TS2345: Argument of type 'typeof Form' is not assignable to parameter of type 'ComponentType<IFromProps & InjectedFormProps<{ [x: string]: any; }, IFromProps>>'.
  Type 'typeof Form' is not assignable to type 'StatelessComponent<IFromProps & InjectedFormProps<{ [x: string]: any; }, IFromProps>>'.
    Type 'typeof Form' provides no match for the signature '(props: IFromProps & InjectedFormProps<{ [x: string]: any; }, IFromProps> & { children?: ReactNode; }, context?: any): ReactElement<any>'.

如果我只将 any 留在 reduxForm 装饰器上,我会收到此错误。

TS2339: Property 'fields' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<{}, ComponentState, any>> & Readonly<{ children?: ReactNode; }> & Readonly<{}>'.

无论其价值如何,我都不希望任何人能够解决这些错误,因为发生的事情太多了。我自己也花了几个小时试图消除这些错误,这只是一场打地鼠游戏。

所以,在一天结束时,如果我能简单地覆盖 TS 认为导出的组件是什么,我会很高兴。

好的,我这样做了:

class Form extends Component<InjectedFormProps & IFromProps, {}> .....

然后像这样导出:

export default connect<{}, {}, IFromProps, {}>(
  mapStateToProps,
  mapDispatchToProps,
)(reduxForm(formConfig)(Form));

现在我的组件验证了正确的道具!