首页 文章

使用Redux-Form与React-Redux连接时的TypeScript错误

提问于
浏览
4

我爱上了TypeScript,直到我发现Redux-Form与React-Redux之间存在一些非常令人沮丧的不兼容性 .

我的目标是使用react-redux connect 装饰器包装 reduxForm 装饰组件 - 这种模式在babel配置中一直对我有用,并且似乎遵循HOC方法 . 这是一个例子:

import * as React from 'react';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { reduxForm, Field, InjectedFormProps } from 'redux-form';

interface SampleFormData {
  username: string;
}

interface SampleFormProps {
  saveData: (data: SampleFormData) => void;
}

type AllSampleFormProps = SampleFormProps & InjectedFormProps<SampleFormData>;

const SampleForm: React.SFC<AllSampleFormProps> = (props) => (
  <form onSubmit={props.handleSubmit(props.saveData)}>
    <Field name="username" component="input" />
  </form>
);

const DecoratedSampleForm = reduxForm<SampleFormData>({ form: "sampleForm" })(SampleForm);

export default connect(
  () => ({}),
  (dispatch) => ({
    saveData: (data: SampleFormData) => dispatch({ type: "SAVE_DATA", data })
  })
)(DecoratedSampleForm);

这是TypeScript抛出的错误:

> Argument of type 'DecoratedComponentClass<SampleFormData,
> Partial<ConfigProps<SampleFormData, {}>>>' is not assignable to
> parameter of type 'ComponentType<{ saveData: (data: SampleFormData) =>
> { type: string; data: SampleFormData; }; }>'.
> 
> Type 'DecoratedComponentClass<SampleFormData,
> Partial<ConfigProps<SampleFormData, {}>>>' is not assignable to type
> 'StatelessComponent<{ saveData: (data: SampleFormData) => { type:
> string; data: SampleFormData; };...'.
> 
> Type 'DecoratedComponentClass<SampleFormData,
> Partial<ConfigProps<SampleFormData, {}>>>' provides no match for the
> signature '(props: { saveData: (data: SampleFormData) => { type:
> string; data: SampleFormData; }; } & { children?: ReactNode; },
> context?: any): ReactElement<any>'.

有没有人找到一个让react-redux接受 DecoratedComponentClass 类型的解决方案?我发现suggestion使用"middle"组件,但是我发现这会产生比键入表单道具更多的问题 .

2 回答

  • 8

    一个更高的组件接口声明做了包装连接的技巧,使用装饰器将Type与组件状态和props类型连接起来 .

    connect.ts

    import * as React from "react";
    import {
      connect as originalConnect,
      MapStateToPropsParam,
      MergeProps,
      Options
    } from "react-redux";
    import { IState } from "./index";
    
    export interface IDisPatchProps {
      [key: string]: () => void;
    }
    
    export type InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> = <
      TComponent extends React.ComponentType<TInjectedProps & TNeedsProps>
    >(
      component: TComponent
    ) => TComponent;
    
    export interface IConnectProps {
      <TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
        mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, IState>,
        mapDispatchToProps?: IDisPatchProps
      ): InferableComponentEnhancerWithProps<
        TStateProps & TDispatchProps,
        TOwnProps
      >;
    
      <TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, TMergedProps = {}>(
        mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, IState>,
        mapDispatchToProps?: IDisPatchProps,
        mergeProps?: MergeProps<
          TStateProps,
          TDispatchProps,
          TOwnProps,
          TMergedProps
        >,
        options?: Options<TStateProps, TOwnProps, TMergedProps>
      ): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>;
    }
    
    declare module "react-redux" {
      // tslint:disable-next-line
      interface Connect extends IConnectProps {}
    }
    
    export const connect = originalConnect as IConnectProps;
    
    
     ***ClassFile***
    
     @connect(
      (state: IState): IStateProps => ({
        count: state.counter.count,
        isLoading: state.counter.isLoading
      }),
      {
        decrement,
        increment
      }
    )
    export default class MyApp
    

    链接:https://github.com/TomasHubelbauer/react-redux-typescript-connect-decorator-demo/blob/master/my-app/src/connect.ts

    积分转到: TomasHubelbauer https://github.com/TomasHubelbauer

  • 1

    对于遇到此问题的任何人,我发现通过提供带有空 TStatePropsTDispatchProps 对象的connect语句,我能够解除错误 .

    interface SampleFormData {
      username: string;
    }
    
    interface SampleFormProps {
      saveData: (data: SampleFormData) => void;
    }
    
    type AllSampleFormProps = SampleFormProps & InjectedFormProps<SampleFormData>;
    
    const SampleForm: React.SFC<AllSampleFormProps> = (props) => (
      <form onSubmit={props.handleSubmit(props.saveData)}>
        <Field name="username" component="input" />
      </form>
    );
    
    const DecoratedSampleForm = reduxForm<SampleFormData>({ form: "sampleForm" })(SampleForm);
    
    export default connect<{},{}>(
      () => ({}),
      (dispatch) => ({
        saveData: (data: SampleFormData) => dispatch({ type: "SAVE_DATA", data })
      })
    )(DecoratedSampleForm);
    

    这样做的一个缺点是它迫使我们盲目地提供连接道具,但我觉得这是一个比写一个覆盖@types声明更优雅的解决方案 .

    为了解决这个缺点,我能够通过提供正确的接口与空对象的连接来验证类型;但是,此方法只能临时完成以检查绑定,因为它无法解决 DecoratedComponentClass 错误 .

    export default connect<{}, SampleFormProps, InjectedFormProps<SampleFormData>>(
      () => ({}),
      (dispatch) => ({
        saveData: (data: SampleFormData) => dispatch({ type: "SAVE_DATA", data })
      })
    )(DecoratedSampleForm);
    

相关问题