react redux typescript:连接缺少类型错误

IT技术 reactjs typescript redux react-redux
2021-05-07 07:08:30

我正在尝试使用 typescript 获取 react-redux 应用程序,但我一直在围绕相同的错误盘旋。以下代码编译并产生预期结果

// State definition
interface HelloWorldState {
    clickCount: number
}
interface AppState extends HelloWorldState {}


// Props definitions
interface HelloWorldProps {
    count: number
}


// Actions
const CLICK = 'CLICK';
const click = () => {return {type:CLICK}};


// Reducers
function clickCount(state:number = 0, action:Action) {
    if (typeof state === 'undefined') {
        return 0;
    }
    switch (action.type) {
        case CLICK:
            return state + 1;
        default:
            return state;
    }
}
let rootReducer = combineReducers({
    clickCount
});


// Store
let store = createStore(rootReducer);


// Components
class HelloWorld extends React.Component<any, any> {
    render() {
        return <div onClick={this.handleClick.bind(this)}>Hello world "{this.props.count}"</div>
    }

    handleClick() {
        store.dispatch(click())
    }
}


// Container components
const mapStateToProps = (state:AppState):HelloWorldState => {
    return Immutable.fromJS({
        count: state.clickCount
    })
};
const ConnectedHelloWorld = connect(
    mapStateToProps
)(HelloWorld);

render(
    <Provider store={store}>
        <ConnectedHelloWorld/>
    </Provider>,
    container
);

伟大的!但我使用的是 TypeScript,因为我想在编译时进行类型检查。类型检查最重要的是 state 和 props。所以,而不是class HelloWorld extends React.Component<any, any>,我想做class HelloWorld extends React.Component<HelloWorldProps, any>但是,当我这样做时,我从调用中得到以下编译错误render

TS2324:Property 'count' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & HelloWorldProps & { children?: React...'.

我真的不明白为什么。count IS目前在HelloWordProps定义,它是由减速机提供的,所以我应该没事吧?类似的问题表明这是一个推理问题,我应该声明调用的类型connect,但我似乎无法找出如何

包.json

{
  "name": "reacttsx",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "ts-loader": "^1.3.3",
    "typescript": "^2.1.5",
    "webpack": "^1.14.0",
    "typings": "^2.1.0"
  },
  "dependencies": {
    "es6-promise": "^4.0.5",
    "flux": "^3.1.2",
    "immutable": "^3.8.1",
    "isomorphic-fetch": "^2.2.1",
    "jquery": "^3.1.1",
    "react": "^15.4.2",
    "react-dom": "^15.4.2",
    "react-redux": "^5.0.2",
    "redux": "^3.6.0",
    "redux-logger": "^2.7.4",
    "redux-thunk": "^2.2.0"
  }
}

打字.json

{
  "dependencies": {
    "flux": "registry:npm/flux#2.1.1+20160601175240",
    "immutable": "registry:npm/immutable#3.7.6+20160411060006",
    "react": "registry:npm/react#15.0.1+20170104200836",
    "react-dom": "registry:npm/react-dom#15.0.1+20160826174104",
    "react-redux": "registry:npm/react-redux#4.4.0+20160614222153",
    "redux-logger": "registry:dt/redux-logger#2.6.0+20160726205300",
    "redux-thunk": "registry:npm/redux-thunk#2.0.0+20160525185520"
  },
  "globalDependencies": {
    "es6-promise": "registry:dt/es6-promise#0.0.0+20160726191732",
    "isomorphic-fetch": "registry:dt/isomorphic-fetch#0.0.0+20170120045107",
    "jquery": "registry:dt/jquery#1.10.0+20170104155652",
    "redux": "registry:dt/redux#3.5.2+20160703092728",
    "redux-thunk": "registry:dt/redux-thunk#2.1.0+20160703120921"
  }
}

更新

由于它抱怨count丢失了,我尝试更新到

render(
    <Provider store={store}>
        <ConnectedHelloWorld count={0}/>
    </Provider>,
    container
);

这解决了这个问题。所以问题是编译器不知道Provider正在提供计数。提供者使用商店。存储应该具有由容器组件clickCount映射到count

我注意到我忘记了商店的初始状态。因此,即使类型已检出,状态也会为空。我将其更新为

// Store
let initialState:AppState = {clickCount: 0};
let store = createStore(rootReducer, initialState);

现在我确定clickCount商店中的设置正确。所以我希望mapStateToProps函数按照指定的方式获取AppState并返回HelloWorldProps,然后Provider应该提供计数值。这是真的,但编译器没有看到。

那么如何补救呢?

4个回答

在我的例子中,我像这样在连接函数中为 mapDispatchToProps 参数传递 null,因为我没有为此组件使用调度:

export default connect(mapStateToProps, null)(MainLayout);

将其更改为省略 mapDispatchToProps 参数为我修复了它

export default connect(mapStateToProps)(MainLayout);

这就是我在 Typescript Redux 应用程序中的做法(根据您的代码进行了调整,但未经过测试)

编辑下面的评论

  1. connect带有连接组件 ( ConnectedHelloWorldProps)props的类型

    const ConnectedHelloWorld:React.ComponentClass<ConnectedHelloWorldProps>  = 
        connect<any,any,HelloWorldProps>(mapStateToProps)(HelloWorld)
    
    interface ConnectedHelloWorldProps { }
    
    interface HelloWorldProps extends ConnectedHelloWorldProps {
        count: number
        ....
    }
    
  2. 使用所连接的组件和它ConnectedHelloWorldProps在propsProvider

    <Provider store={store}>
        <ConnectedHelloWorld/>
    </Provider>
    

注意:这适用于这些类型

"@types/react": "^0.14.52",
"@types/react-dom": "^0.14.19",
"@types/react-redux": "^4.4.35",
"@types/redux-thunk": "^2.1.32",

ConnectedHellowWorldProps 这里并不是真的需要,因为它是一个空的界面,但在现实世界的场景中,它可能包含一些props。

基本原则是:ConnectedHelloWorldProps包含需要在Provider级别传递的内容。mapStateToProps和/或中mapDispatchToPropsHelloWorldProps用任何需要的东西来丰富实际的组件

Redux Typescript 类型是一种野兽,但上面显示的内容应该足够了。

export declare function connect<TStateProps, TDispatchProps, TOwnProps>(
mapStateToProps: FuncOrSelf<MapStateToProps<TStateProps, TOwnProps>>,
 mapDispatchToProps?: FuncOrSelf<MapDispatchToPropsFunction<TDispatchProps, TOwnProps> | MapDispatchToPropsObject>): ComponentDecorator<TStateProps & TDispatchProps, TOwnProps>;


 interface ComponentDecorator<TOriginalProps, TOwnProps> {
    (component: ComponentClass<TOriginalProps> | StatelessComponent<TOriginalProps>): ComponentClass<TOwnProps>;
 }

你的问题是这样HelloWorldState定义的

interface HelloWorldState {
  clickCount: number
}

你想要返回的propsmapStateToProps

{ 
  count: state.clickCount
}

但是您将返回类型覆盖为HelloWorldState,因此返回类型不包含countclickCount


fromJS 打破类型安全

ImmutableJS.fromJS 使用 TypeScript 类型推断效果很差:

const state = Immutable.fromJS({
  count: 0
})

state是 type ,any因此在将其分配为 type 的返回值时不会出现任何错误HelloWorldState


mapStateToProps 应该返回一个简单的对象

mapStateToProps 可以返回一个简单的对象,因为您不会直接从组件编辑此状态:

const mapStateToProps = (state: AppState): HelloWorldState => {
  return {
    count: state.clickCount
  }
}

在这里你会遇到使用 ImmutableJS 时没有的错误,告诉你你不能分配{ count: number }{ clickCount: number }.

所以只需删除返回类型,类型推断就会完成这项工作,或者添加正确的类型。

const mapStateToProps = (state: AppState) => {
  return {
    count: state.clickCount
  }
}

与 Monolite 共享静态类型树结构

我还建议您使用Monolite,它是一组用 TypeScript 编写的简单函数,旨在与 Redux 状态一起使用。

它还允许您使用简单的 JavaScript 对象定义状态,并通过简单的函数更新状态。

import { set } from 'monolite'

const state = {
  clickCount: 0
}

const newState = set(state, _ => _.clickCount)(value => value + 1)

PS我是 Monolite 的作者

我在使用 connect() 编写组件类时遇到了类似的问题。就我而言,props中存在循环依赖

const mapDispatchToProps = (dispatch: Dispatch) => {/* return something here */}
const mapStateToProps = (state: IState, ownProps: AllProps) => {/* return something here */}
type AllProps = {
someExtraProp: string;
} & ReturnType<typeof mapDispatchToProps> & ReturnType<typeof mapStateToProps>;

正如你所看到的,这导致了 AllProps 和 mapStateToProps 之间的循环类型依赖。我分离了 OwnProps 代码,现在看起来像这样:

const mapDispatchToProps = (dispatch: Dispatch) => {/* return something here */}
const mapStateToProps = (state: IState, ownProps: OwnProps) => {/* return something here */}
const OwnProps = {
someExtraProp: string;
};
type AllProps = OwnProps & ReturnType<typeof mapDispatchToProps> & ReturnType<typeof mapStateToProps>;

现在,connect 调用不会抛出任何类型错误。希望这可以帮助有类似问题的人。