私はReact / Redux / Redux-ThunkプロジェクトでTypeScriptをいじっていますがconnect
、コンポーネントを作成した後、接続プロセスがそうではないため、キャストせずにそれを賢明に使用することができないように見えるこの障害にぶつかり続けています接続操作によってプロパティ要件の一部またはすべてが満たされていることを型システムに伝えることができないようです。たとえば、次のコンポーネント/タイプなどについて考えてみます。
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)すべての小道具をオプションにします。私がサードパーティから見た多くのコンポーネントにはすべてがオプションですが、私の経験では、すべてがオプションであると、いたるところに多くの定型的なゼロチェックが発生し、コードが読みにくくなります。
アイデア#2)操作で入力されていないプロパティにキャストしReact.ComponentClass<P,S>
て追加のタイプを作成します。キャストは明らかに機能しますが、同期を保つための3つのセット(元の小道具タイプ、andリスト、および「残りの小道具」タイプ)があります。このアプローチは、冗長で、エラーが発生しやすく、また、他の潜在的に有用なタイプ情報も消去します。connect
mapStateToProps
mapDispatchToProps
connect
タイプの観点からedコンポーネントを管理するためのより良い方法はありますか?
もう少し掘り下げた後、私はこれを理解したと思います。質問に示されている形式(およびconnect
、型定義ファイルに呼び出される可能性のある呼び出し/型パターンが12個あることに注意してください-これは1つだけです)、に4つの型パラメーターがありconnect
ます。それらは以下を表すように見えます:
mapStateToProps
パラメータを入力するプロパティを含むタイプconnect
。(ちなみにmapStateToProps
関数の戻り値の型でもあります)mapDispatchToProps
パラメータを入力するプロパティを含むタイプconnect
。(ちなみにmapDispatchToProps
関数の戻り値の型でもあります)connect
(これは私の混乱があった場所です)。TOwnPropsは、およびへのパラメーターのタイプでもあります。ownProps
mapStateToProps
mapDispatchToProps
No. 3が呼び出されるのTOwnProps
はownProps
、mapStateToProps
andmapDispatchToProps
関数へのパラメーターとの関連付けを意味するため、少し混乱します。ownProps
実際に使う機会がなかったので、実際にはそれ以上のものがあることをすぐには理解しませんでした。さらに掘り下げてみると、次のようになりconnect
ます。
InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>
その定義は次のとおりです。
export interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
<C extends ComponentType<Matching<TInjectedProps, GetProps<C>>>>(
component: C
): ConnectedComponentClass<C, Omit<GetProps<C>, keyof Shared<TInjectedProps, GetProps<C>>> & TNeedsProps>
}
TStateProps & TDispatchProps
と呼ばれ、TInjectedProps
とTOwnProps
呼ばれるのを見ると、TNeedsProps
物事をもう少し焦点を合わせるのに役立ちました。TStateProps & TDispatchProps
プロパティはでラップ成分に「注入」されconnect
、およびTOwnProps
特性は、接続されたコンポーネントの消費者からのラッパーが依然として「必要」ということです。
私が持っていた他の認識はconnect<FooDataProps, FooDispatchProps, FooProps, Model>(mapStateToProps, mapDispatchToProps)
、3番目の型パラメーターが(意味的に)「すべての小道具、状態、またはディスパッチ」を表すことになっている場合、型システムは簡単に達成できたので、質問での方法(つまり)は意味がないということでしたそれによる&
INGのFooDataProps
とFooDispatchProps
、それはのparam#1とのparam#2となっていること。私が使用していたので、その3番目のタイプのパラメーターを持つことによって伝えられる新しい情報はありません。
Matt McCutchenの答えは、役に立ちますが、と関数のパラメーターTOwnProps
に関するの役割にのみ焦点を当てていownProps
ます。彼は、私がこれらの関数でパラメーターを使用しないことを正しく観察し、を渡すことを提案しました。その答えは、それが説明するコンテキストには正しいように見えますが、高階コンポーネントが受け入れることができる小道具の決定要因としての他の役割を無視しています。mapStateToProps
mapDispatchToProps
ownProps
{}
TOwnProps
connect
ここでの要約のポイントは、マップ関数へのパラメーターのTOwnProps
タイプとしてだけでなく、ラップされた/接続されたコンポーネントのコンシューマーによって設定されたままのプロパティをキャプチャするタイプとしてもownProps
、ここで二重の義務を果たしているということです。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加