【发布时间】:2018-10-25 19:12:42
【问题描述】:
我一直在 React/Redux/Redux-Thunk 项目中使用 TypeScript,我一直遇到这个问题,在 connecting 一个组件之后,如果不强制转换它似乎不可能明智地使用它,因为连接过程似乎无法向类型系统传达连接操作已满足部分或全部属性要求。例如,考虑这些组件/类型/等:
import * as React from 'react';
import {connect} from "react-redux";
import {Action, bindActionCreators, Dispatch} from "redux";
import {ThunkDispatch} from "redux-thunk";
// Our store model
interface Model {
name: string,
}
// Types for our component's props
interface FooDataProps {
name: string // Single, required, string property
}
interface FooDispatchProps {
onClick: React.MouseEventHandler<HTMLButtonElement>, // Single, required, event handler.
}
interface FooProps extends FooDataProps, FooDispatchProps { // Union the two types
}
// Make our first component...
function TrivialComponent(props: FooProps) {
return (<button onClick={props.onClick}>{props.name}</button>);
}
// Now make a Redux "container" that wires it to the store...
const mapStateToProps = (state: Model): FooDataProps => { return { name: state.name }; };
const mapDispatchToProps = (dispatch: Dispatch): FooDispatchProps => {
return bindActionCreators({onClick: doStuff}, dispatch);
};
// Wire it up with all the glory of the heavily-genericized `connect`
const ConnectedTrivialComponent = connect<FooDataProps, FooDispatchProps, FooProps, Model>(mapStateToProps, mapDispatchToProps)(TrivialComponent);
// Then let's try to consume it
function ConsumingComponent1() {
// At this point, I shouldn't need to provide any props to the ConnectedTrivialComponent -- they're
// all being provided by the `connect` hookup, but if I try to use the tag like I'm doing here, I
// get this error:
//
// Error:(53, 10) TS2322: Type '{}' is not assignable to type 'Readonly<Pick<FooProps, never> & FooProps>'.
// Property 'name' is missing in type '{}'.
//
return (<ConnectedTrivialComponent/>)
}
// If I do something like this:
const ConnectedTrivialComponent2 = ConnectedTrivialComponent as any as React.ComponentClass<{}, {}>;
// Then let's try to consume it
function ConsumingComponent2() {
// I can do this no problem.
return (<ConnectedTrivialComponent2/>)
}
// Handler...
const doStuff = (e: React.MouseEvent<HTMLButtonElement>) => (dispatch: ThunkDispatch<Model, void, Action>, getStore: () => Model) => {
// Do stuff
};
好的,所以,在考虑这个问题时,我已经通过了一些想法:
想法 #1)让所有的 props 都是可选的。 我从第三方看到的很多组件都是可选的,但是根据我的经验,让所有的都是可选的会导致很多样板的 nil-check all在这个地方,并使代码更难阅读。
想法#2)转换为React.ComponentClass<P,S>,并为connect 操作未填充的任何属性创建其他类型。演员阵容显然有效,但现在你有三组东西要相互保持同步(原始道具类型,mapStateToProps 和mapDispatchToProps 列表,以及“剩余道具”类型。)这种方法感觉冗长、容易出错,而且还会删除其他可能有用的类型信息。
有没有更好的方法来管理connected 组件的类型?
【问题讨论】:
标签: reactjs typescript redux redux-thunk