React redux typescript:连接缺少类型错误
React redux typescript: connect has missing type error
我正在尝试让 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,因为我想在编译时进行类型检查。类型检查最重要的是状态和道具。所以我想 class HelloWorld extends React.Component<HelloWorldProps, any>
而不是 class HelloWorld extends React.Component<any, any>
。但是,当我这样做时,我从对 render
的调用中得到以下编译错误
TS2324:Property 'count' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & HelloWorldProps & { children?: React...'.
我不太明白为什么。 count
IS 出现在 HelloWordProps
定义中,它是由 reducer 提供的,所以我应该没问题,对吧?类似的问题表明这是一个推理问题,我应该声明对connect
的调用类型,但我似乎无法找出如何
package.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"
}
}
typings.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
和 return HelloWorldProps
,然后 Provider
应该提供计数值。这是真的,但编译器看不到它。
那么如何补救呢?
这是我在 Typescript Redux 应用程序中的做法(根据您的代码进行了调整,但未经测试)
编辑了下面的评论
键入 connect
以及连接组件的道具 (ConnectedHelloWorldProps
)
const ConnectedHelloWorld:React.ComponentClass<ConnectedHelloWorldProps> =
connect<any,any,HelloWorldProps>(mapStateToProps)(HelloWorld)
interface ConnectedHelloWorldProps { }
interface HelloWorldProps extends ConnectedHelloWorldProps {
count: number
....
}
在 Provider
中使用连接组件及其 ConnectedHelloWorldProps
道具
<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
这里并不是真正需要的,因为它是一个空接口,但在现实世界的场景中它可能包含一些道具。
基本原则是这样的:ConnectedHelloWorldProps
包含需要在 Provider 级别传递的内容。在 mapStateToProps
and/or mapDispatchToProps
中,用需要的任何东西丰富实际的组件 HelloWorldProps
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
}
你想要从 mapStateToProps
return 获得的道具是
{
count: state.clickCount
}
但您将 return-type 覆盖为 HelloWorldState
,因此 return 类型不包含 count
,但包含 clickCount
.
fromJS
破坏类型安全
ImmutableJS.fromJS
使用 TypeScript 类型推断时效果很差:
const state = Immutable.fromJS({
count: 0
})
此处 state
是 any
类型,因此将其分配为 HelloWorldState
.[=31= 类型的 return 值时没有任何错误]
mapStateToProps
应该 return 一个简单的对象
mapStateToProps
可以 return 一个简单的对象,因为您不会直接从组件编辑此状态:
const mapStateToProps = (state: AppState): HelloWorldState => {
return {
count: state.clickCount
}
}
这里会出现使用ImmutableJS时没有的错误,告诉你不能将{ count: number }
赋值给{ clickCount: number }
。
所以只需删除 return 类型,type-inference 即可完成工作,或者添加正确的类型。
const mapStateToProps = (state: AppState) => {
return {
count: state.clickCount
}
}
Statically-typed 树 structural-sharing 与 Monolite
我还建议您使用 Monolite,这是一组用 TypeScript 编写的简单函数,旨在与 Redux 状态一起使用。
它还允许您使用简单的 JavaScript 对象定义状态,并通过简单的函数更新状态。
import { set } from 'monolite'
const state = {
clickCount: 0
}
const newState = set(state, _ => _.clickCount)(value => value + 1)
P.S. 我是 Monolite 的作者
我在使用 connect() 编写组件 class 时遇到了类似的问题。就我而言,道具中存在循环依赖
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 函数中为 mapDispatchToProps 参数传递了 null,因为我没有为此组件使用分派:
export default connect(mapStateToProps, null)(MainLayout);
将其更改为仅省略 mapDispatchToProps 参数为我修复了它
export default connect(mapStateToProps)(MainLayout);
我正在尝试让 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,因为我想在编译时进行类型检查。类型检查最重要的是状态和道具。所以我想 class HelloWorld extends React.Component<HelloWorldProps, any>
而不是 class HelloWorld extends React.Component<any, any>
。但是,当我这样做时,我从对 render
TS2324:Property 'count' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & HelloWorldProps & { children?: React...'.
我不太明白为什么。 count
IS 出现在 HelloWordProps
定义中,它是由 reducer 提供的,所以我应该没问题,对吧?类似的问题表明这是一个推理问题,我应该声明对connect
的调用类型,但我似乎无法找出如何
package.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"
}
}
typings.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
和 return HelloWorldProps
,然后 Provider
应该提供计数值。这是真的,但编译器看不到它。
那么如何补救呢?
这是我在 Typescript Redux 应用程序中的做法(根据您的代码进行了调整,但未经测试)
编辑了下面的评论
键入
connect
以及连接组件的道具 (ConnectedHelloWorldProps
)const ConnectedHelloWorld:React.ComponentClass<ConnectedHelloWorldProps> = connect<any,any,HelloWorldProps>(mapStateToProps)(HelloWorld) interface ConnectedHelloWorldProps { } interface HelloWorldProps extends ConnectedHelloWorldProps { count: number .... }
在
中使用连接组件及其Provider
ConnectedHelloWorldProps
道具<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
这里并不是真正需要的,因为它是一个空接口,但在现实世界的场景中它可能包含一些道具。
基本原则是这样的:ConnectedHelloWorldProps
包含需要在 Provider 级别传递的内容。在 mapStateToProps
and/or mapDispatchToProps
中,用需要的任何东西丰富实际的组件 HelloWorldProps
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
}
你想要从 mapStateToProps
return 获得的道具是
{
count: state.clickCount
}
但您将 return-type 覆盖为 HelloWorldState
,因此 return 类型不包含 count
,但包含 clickCount
.
fromJS
破坏类型安全
ImmutableJS.fromJS
使用 TypeScript 类型推断时效果很差:
const state = Immutable.fromJS({
count: 0
})
此处 state
是 any
类型,因此将其分配为 HelloWorldState
.[=31= 类型的 return 值时没有任何错误]
mapStateToProps
应该 return 一个简单的对象
mapStateToProps
可以 return 一个简单的对象,因为您不会直接从组件编辑此状态:
const mapStateToProps = (state: AppState): HelloWorldState => {
return {
count: state.clickCount
}
}
这里会出现使用ImmutableJS时没有的错误,告诉你不能将{ count: number }
赋值给{ clickCount: number }
。
所以只需删除 return 类型,type-inference 即可完成工作,或者添加正确的类型。
const mapStateToProps = (state: AppState) => {
return {
count: state.clickCount
}
}
Statically-typed 树 structural-sharing 与 Monolite
我还建议您使用 Monolite,这是一组用 TypeScript 编写的简单函数,旨在与 Redux 状态一起使用。
它还允许您使用简单的 JavaScript 对象定义状态,并通过简单的函数更新状态。
import { set } from 'monolite'
const state = {
clickCount: 0
}
const newState = set(state, _ => _.clickCount)(value => value + 1)
P.S. 我是 Monolite 的作者
我在使用 connect() 编写组件 class 时遇到了类似的问题。就我而言,道具中存在循环依赖
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 函数中为 mapDispatchToProps 参数传递了 null,因为我没有为此组件使用分派:
export default connect(mapStateToProps, null)(MainLayout);
将其更改为仅省略 mapDispatchToProps 参数为我修复了它
export default connect(mapStateToProps)(MainLayout);