質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

4504閲覧

reduxのtypescript対応。react-reduxのconnectの返り値の型を変更したい

threeaster

総合スコア14

Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2017/06/04 01:55

編集2017/06/05 06:39

###前提・実現したいこと
http://redux.js.org/docs/basics/
をtypescriptでやっているときに、おそらく型によるエラーが出て、解決できません。
(http://redux.js.org/docs/basics/UsageWithReact.html を実装しているときにエラーが出ました。)

###該当のソースコード
上のチュートリアルで作成したものから必要だと思われる部分を抜粋したものが以下となります。

typescript

1import * as React from 'react' 2import { render } from 'react-dom' 3import { createStore, Dispatch } from 'redux' 4import { Provider, connect } from 'react-redux' 5import * as _ from 'lodash' 6 7//actions 8interface SetVisibilytyAction{ 9 type: 'SET_VISIBILITY_FILTER', 10 filter: string 11} 12 13function setVisibilityFilter(filter: string) : SetVisibilytyAction{ 14 return { type: 'SET_VISIBILITY_FILTER', filter: filter }; 15} 16 17//reducers 18interface State{ 19 visibilityFilter: string 20} 21 22const initialState = { 23 visibilityFilter: 'SHOW_ALL' 24} 25 26function todoApp(state: State = initialState, action: SetVisibilytyAction){ 27 switch(action.type){ 28 case 'SET_VISIBILITY_FILTER': 29 return _.assign({}, state, { visibilityFilter: action.filter }) 30 default: 31 return state 32 } 33} 34 35//components 36interface LinkProp{ 37 active: boolean, 38 children: string, 39 onClick: () => void 40} 41 42const Link = ({active, children, onClick}: LinkProp )=> { 43 if(active){ 44 return <span>{children}</span> 45 } 46 return ( 47 <a href="#" onClick={ e => {e.preventDefault(); onClick()} }> 48 {children} 49 </a> 50 ) 51} 52 53//containers 54interface FilterLinkProp{ 55 filter: string, 56 children: string, 57 onClick: () => void 58} 59 60const mapStateToProps = (state: State, ownProps: FilterLinkProp) => { 61 return { 62 active: ownProps.filter === state.visibilityFilter 63 } 64} 65 66const mapDispatchToProps = (dispatch: Dispatch<SetVisibilytyAction>, ownProps: FilterLinkProp) => { 67 return { 68 onClick: () => { 69 dispatch(setVisibilityFilter(ownProps.filter)) 70 } 71 } 72} 73 74const FilterLink = connect( 75 mapStateToProps, 76 mapDispatchToProps 77)(Link) 78 79//store 80let store = createStore(todoApp) 81 82render( 83 <Provider store={store}> 84 <div> 85 <FilterLink filter="SHOW_ALL"> 86 All 87 </FilterLink> 88 </div> 89 </Provider>, 90 document.getElementById('root') 91)

###発生している問題・エラーメッセージ
そして、上記をwebpackした時のエラーが以下となります。

ERROR in ./js/dev/index.tsx (85,19): error TS2322: Type '{ filter: "SHOW_ALL"; children: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<LinkProp, ComponentState>> & Readonly<{ ...'. Type '{ filter: "SHOW_ALL"; children: string; }' is not assignable to type 'Readonly<LinkProp>'. Property 'active' is missing in type '{ filter: "SHOW_ALL"; children: string; }'.

###試したこと
FilterLinkのプロパティをLinkPropに合わせたらうまく行きました

render( <Provider store={store}> <div> <FilterLink active={true} onClick={() => {}}> All </FilterLink> </div> </Provider>, document.getElementById('root') )

これよりFilterLinkPropLinkProp型になっているのだと思うのですが、これをFilterLinkPropにする方法がわかりません。
もちろん

const FilterLink = connect( mapStateToProps, mapDispatchToProps )(Link) as React.ComponentClass<FilterLinkProp>

のようにキャストすることはできませんでした

###補足情報(言語/FW/ツール等のバージョンなど)
package.json

{ "name": "threeaster", "version": "1.0.0", "main": "index.js", "dependencies": { "@types/lodash": "^4.14.64", "@types/react": "^15.0.24", "@types/react-dom": "^15.5.0", "@types/react-redux": "^4.4.41", "@types/redux": "^3.6.0", "lodash": "^4.17.4", "react": "^15.5.4", "react-dom": "^15.5.4", "react-redux": "^5.0.5", "redux": "^3.6.0" }, "devDependencies": { "ts-loader": "^2.0.3", "typescript": "^2.3.2", "webpack": "^2.5.1" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }

tsconfig.json(コメントアウト削除)は

{ "compilerOptions": { "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */ "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ "strict": true /* Enable all strict type-checking options. */ } }

webpack.config.js

module.exports = { entry: "./js/dev/index.tsx", output: { path: __dirname + '/js/dist', filename: "bundle.js" }, resolve: { extensions: [".ts", ".tsx", ".js", ".json"] }, module: { loaders: [ { test: /\.tsx?$/, loader: "ts-loader" } ] }, externals: { "react": "React", "react-dom": "ReactDOM" } }

その他環境は
mac OSX Yosemite 10.10.5
tsc --version => Version 2.3.2
npm --version => 3.10.10
です。
よろしくお願いします

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

###エラーの原因

react-reduxの定義の所の

TypeScript

1interface ComponentDecorator<TMergedProps> { 2 <TOwnProps>(component: Component<(TOwnProps & TMergedProps) | TOwnProps>): ComponentClass<TOwnProps>; 3}

の部分で、TOwnPropsに対する型推論がうまくいかないのが原因のようです。マージした物と推論できるように、

TypeScript

1//components 2interface LinkProp{ 3 filter: string, 4 active: boolean, 5 children: string, 6 onClick: () => void 7} 8 9//containers 10interface FilterLinkProp{ 11 filter: string, 12 children: string 13}

としてみましたが、指定したコンポーネントのpropsの型自体をマージされた物と見なさず、TOwnPropsLinkPropだと推論してしまい、同じpropsの型になってしまいます。

たぶん、connectownProps付きでmapStateToPropsmapStateToProps を渡す場合の定義の所の

export declare function connect<TStateProps, TDispatchProps, TOwnProps>( mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps>, mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps> ): ComponentDecorator<TStateProps & TDispatchProps>;

export declare function connect<TStateProps, TDispatchProps, TOwnProps>( mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps>, mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps> ): ComponentMergeDecorator<TStateProps & TDispatchProps, TOwnProps>;

の間違いなんじゃないかなと思います。(ちょっとよくわからないのでIssue投げるべきか、プルリクすべきかは悩みどころ)

全く一緒ではありませんが、connect周りの定義についてIssueが報告されていますので、そのうち解決するのかも知れません。

react-redux connect() breaks component creation · Issue #16137 · DefinitelyTyped/DefinitelyTyped
react-redux's connect can output too-lenient type React.ComponentClass<{}> · Issue #8787 · DefinitelyTyped/DefinitelyTyped

###解決方法

とりあえずの解決方法を三つほど見つけました。なお、LinkPropFilterLinkPropは上で修正した物に修正済みです(そうでないとエラーになります)。

####その1: 型指定

TypeScript

1const FilterLink = connect( 2 mapStateToProps, 3 mapDispatchToProps 4)<FilterLinkProp>(Link)

問題はconnect()の戻り値自体が総称型であり、その型推論に失敗することです。ですので、推論できないときはちゃんと指定してあげます。書き方に違和感があるような気もしますが、これが一番良いかもしれません。

####その2: キャスト

TypeScript

1const FilterLink = connect( 2 mapStateToProps, 3 mapDispatchToProps 4)(Link) as React.ComponentClass<FilterLinkProp>

色々推論することは諦めてキャストしてしまいます。TSXではキャストに<>が使えずasでする必要があることに注意です。TypeScriptの型推論は時々失敗するような気がしますので、部分的なキャストは仕方が無いと割り切るしか無いと思います。最終手段anyにキャストが必要になるときもありますので。

なお、キャストを試してうまくいかなかったのは、LinkPropFilterPropと追加のpropsとマージされた物になっておらず、また、FilterPropに余計なonClickが付いていたことの所為と推測されます。

####その3: 三番目の引数

TypeScript

1const FilterLink = connect( 2 mapStateToProps, 3 mapDispatchToProps, 4 (stateProps, dispatchProps, ownProps) => _.assign({}, ownProps, stateProps, dispatchProps) 5)(Link)

三番目の引数はデフォルトでObject.assign({}, ...)するだけですが、ここに引数を与えるとconnectの定義が変わって、ComponentMergeDecorator<TMergedProps, TOwnProps>が戻り値になります。TOwnPropsを推論する必要がなくなるため、mapStateToProps等で指定した通りに型が指定されます。

注意点として、"target": "es5"の場合、Object.assign()が使えません。lodashを読み込んでいますので、lodashをつかうといいでしょう。

投稿2017/06/05 12:04

編集2017/06/05 12:08
raccy

総合スコア21733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

threeaster

2017/06/05 15:26

LinkPropとFilterLinkPropを修正した上でその1の方法で解決できました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問