From 5c0ee68664799a2b8f2e0e17f180bc24650925db Mon Sep 17 00:00:00 2001 From: rand0me Date: Tue, 13 Jun 2017 18:59:21 +0300 Subject: [PATCH 1/4] Add TypeScript typings --- package.json | 1 + src/preact-redux.d.ts | 167 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 src/preact-redux.d.ts diff --git a/package.json b/package.json index 82bbd1d..259a1c5 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "description": "Wraps react-redux up for Preact, without preact-compat", "main": "dist/preact-redux.js", "module": "dist/preact-redux.esm.js", + "types": "src/preact-redux.d.ts", "jsnext:main": "dist/preact-redux.esm.js", "minified:main": "dist/preact-redux.min.js", "scripts": { diff --git a/src/preact-redux.d.ts b/src/preact-redux.d.ts new file mode 100644 index 0000000..457afac --- /dev/null +++ b/src/preact-redux.d.ts @@ -0,0 +1,167 @@ +import { Component, ComponentConstructor, VNode, render } from 'preact'; +import * as Redux from 'redux'; + +type Store = Redux.Store; +type Dispatch = Redux.Dispatch; +type ActionCreator = Redux.ActionCreator; + +export interface DispatchProp { + dispatch: Dispatch +} + +interface ComponentDecorator { + (component: Component<(TOwnProps & TMergedProps) | TOwnProps, any>): ComponentConstructor; +} + +interface ComponentMergeDecorator { + (component: Component): ComponentConstructor; +} + +/** + * Connects a Preact component to a Redux store. + * + * - Without arguments, just wraps the component, without changing the behavior / props + * + * - If 2 params are passed (3rd param, mergeProps, is skipped), default behavior + * is to override ownProps (as stated in the docs), so what remains is everything that's + * not a state or dispatch prop + * + * - When 3rd param is passed, we don't know if ownProps propagate and whether they + * should be valid component props, because it depends on mergeProps implementation. + * As such, it is the user's responsibility to extend ownProps interface from state or + * dispatch props or both when applicable + * + * @param mapStateToProps + * @param mapDispatchToProps + * @param mergeProps + * @param options + */ +export declare function connect(): ComponentDecorator>; + +export declare function connect( + mapStateToProps: MapStateToPropsParam +): ComponentDecorator & TStateProps>; + +export declare function connect( + mapStateToProps: null | undefined, + mapDispatchToProps: MapDispatchToPropsParam +): ComponentDecorator; + +export declare function connect( + mapStateToProps: MapStateToPropsParam, + mapDispatchToProps: MapDispatchToPropsParam +): ComponentDecorator; + +export declare function connect( + mapStateToProps: MapStateToPropsParam, + mapDispatchToProps: null | undefined, + mergeProps: MergeProps, +): ComponentMergeDecorator; + +export declare function connect( + mapStateToProps: null | undefined, + mapDispatchToProps: MapDispatchToPropsParam, + mergeProps: MergeProps, +): ComponentMergeDecorator; + +export declare function connect( + mapStateToProps: null | undefined, + mapDispatchToProps: null | undefined, + mergeProps: MergeProps, +): ComponentMergeDecorator; + +export declare function connect( + mapStateToProps: MapStateToPropsParam, + mapDispatchToProps: MapDispatchToPropsParam, + mergeProps: MergeProps, +): ComponentMergeDecorator; + +export declare function connect( + mapStateToProps: MapStateToPropsParam, + mapDispatchToProps: null | undefined, + mergeProps: null | undefined, + options: Options +): ComponentDecorator & TStateProps & TOwnProps>; + +export declare function connect( + mapStateToProps: null | undefined, + mapDispatchToProps: MapDispatchToPropsParam, + mergeProps: null | undefined, + options: Options +): ComponentDecorator; + +export declare function connect( + mapStateToProps: MapStateToPropsParam, + mapDispatchToProps: MapDispatchToPropsParam, + mergeProps: null | undefined, + options: Options +): ComponentDecorator; + +export declare function connect( + mapStateToProps: MapStateToPropsParam, + mapDispatchToProps: MapDispatchToPropsParam, + mergeProps: MergeProps, + options: Options +): ComponentMergeDecorator; + +interface MapStateToProps { + (state: any, ownProps?: TOwnProps): TStateProps; +} + +interface MapStateToPropsFactory { + (initialState: any, ownProps?: TOwnProps): MapStateToProps; +} + +type MapStateToPropsParam = MapStateToProps | MapStateToPropsFactory; + +interface MapDispatchToPropsFunction { + (dispatch: Dispatch, ownProps?: TOwnProps): TDispatchProps; +} + +interface MapDispatchToPropsObject { + [name: string]: ActionCreator; +} + +type MapDispatchToProps = + MapDispatchToPropsFunction | MapDispatchToPropsObject; + +interface MapDispatchToPropsFactory { + (dispatch: Dispatch, ownProps?: TOwnProps): MapDispatchToProps; +} + +type MapDispatchToPropsParam = MapDispatchToProps | MapDispatchToPropsFactory; + +interface MergeProps { + (stateProps: TStateProps, dispatchProps: TDispatchProps, ownProps: TOwnProps): TMergedProps; +} + +interface Options { + /** + * If true, implements shouldComponentUpdate and shallowly compares the result of mergeProps, + * preventing unnecessary updates, assuming that the component is a “pure” component + * and does not rely on any input or state other than its props and the selected Redux store’s state. + * Defaults to true. + * @default true + */ + pure?: boolean; + /** + * If true, stores a ref to the wrapped component instance and makes it available via + * getWrappedInstance() method. Defaults to false. + */ + withRef?: boolean; +} + +export interface ProviderProps { + /** + * The single Redux store in your application. + */ + store?: Store; + children?: VNode; +} + +/** + * Makes the Redux store available to the connect() calls in the component hierarchy below. + */ +export class Provider extends Component { + render(props: ProviderProps): VNode +} \ No newline at end of file From 3ac06e7d6606a4417109d74cfae236453230f9f2 Mon Sep 17 00:00:00 2001 From: rand0me Date: Tue, 13 Jun 2017 19:36:43 +0300 Subject: [PATCH 2/4] Add newline to the end of preact-redux.d.ts --- src/preact-redux.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/preact-redux.d.ts b/src/preact-redux.d.ts index 457afac..cd36b21 100644 --- a/src/preact-redux.d.ts +++ b/src/preact-redux.d.ts @@ -164,4 +164,4 @@ export interface ProviderProps { */ export class Provider extends Component { render(props: ProviderProps): VNode -} \ No newline at end of file +} From 63ad73fff62ac4e9a6d31786bcc3d5c9c1894b11 Mon Sep 17 00:00:00 2001 From: rand0me Date: Mon, 19 Jun 2017 16:42:26 +0300 Subject: [PATCH 3/4] Fixed issue with functional components with AnyComponent type --- src/preact-redux.d.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/preact-redux.d.ts b/src/preact-redux.d.ts index cd36b21..477cc36 100644 --- a/src/preact-redux.d.ts +++ b/src/preact-redux.d.ts @@ -1,20 +1,16 @@ -import { Component, ComponentConstructor, VNode, render } from 'preact'; -import * as Redux from 'redux'; - -type Store = Redux.Store; -type Dispatch = Redux.Dispatch; -type ActionCreator = Redux.ActionCreator; +import { AnyComponent, Component, ComponentConstructor, VNode, render } from 'preact'; +import { Store, Dispatch, ActionCreator } from 'redux'; export interface DispatchProp { dispatch: Dispatch } interface ComponentDecorator { - (component: Component<(TOwnProps & TMergedProps) | TOwnProps, any>): ComponentConstructor; + (component: AnyComponent<(TOwnProps & TMergedProps) | TOwnProps, any>): ComponentConstructor; } interface ComponentMergeDecorator { - (component: Component): ComponentConstructor; + (component: AnyComponent): ComponentConstructor; } /** From 05473edadb52ca23267ee7cfdf30a82999da1b03 Mon Sep 17 00:00:00 2001 From: rand0me Date: Fri, 1 Sep 2017 13:35:29 +0300 Subject: [PATCH 4/4] Fix types according to react-redux types behavior --- src/preact-redux.d.ts | 188 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 152 insertions(+), 36 deletions(-) diff --git a/src/preact-redux.d.ts b/src/preact-redux.d.ts index 477cc36..8b2d4cf 100644 --- a/src/preact-redux.d.ts +++ b/src/preact-redux.d.ts @@ -1,18 +1,43 @@ -import { AnyComponent, Component, ComponentConstructor, VNode, render } from 'preact'; +// Type definitions for preact-redux 2.0.1 +// Definitions by: Qubo , +// Thomas Hasner , +// Kenzie Togami , +// Curits Layne +// Frank Tan +// Daniil Kolesnik +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.4 + +import { AnyComponent, Component, ComponentConstructor, VNode } from 'preact'; import { Store, Dispatch, ActionCreator } from 'redux'; -export interface DispatchProp { - dispatch: Dispatch +// Diff / Omit taken from https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-311923766 +type Diff = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]; +type Omit = Pick>; + +export interface DispatchProp { + dispatch?: Dispatch; } -interface ComponentDecorator { - (component: AnyComponent<(TOwnProps & TMergedProps) | TOwnProps, any>): ComponentConstructor; +interface AdvancedComponentDecorator { + (component: AnyComponent): ComponentConstructor; } -interface ComponentMergeDecorator { - (component: AnyComponent): ComponentConstructor; +// Injects props and removes them from the prop requirements. +// Will not pass through the injected props if they are passed in during +// render. Also adds new prop requirements from TNeedsProps. +export interface InferableComponentEnhancerWithProps { +

( + component: AnyComponent + ): ComponentConstructor & TNeedsProps, {}> } +// Injects props and removes them from the prop requirements. +// Will not pass through the injected props if they are passed in during +// render. +export type InferableComponentEnhancer = + InferableComponentEnhancerWithProps + /** * Connects a Preact component to a Redux store. * @@ -32,97 +57,93 @@ interface ComponentMergeDecorator { * @param mergeProps * @param options */ -export declare function connect(): ComponentDecorator>; +export declare function connect(): InferableComponentEnhancer>; export declare function connect( mapStateToProps: MapStateToPropsParam -): ComponentDecorator & TStateProps>; +): InferableComponentEnhancerWithProps, TOwnProps>; export declare function connect( mapStateToProps: null | undefined, mapDispatchToProps: MapDispatchToPropsParam -): ComponentDecorator; +): InferableComponentEnhancerWithProps; export declare function connect( mapStateToProps: MapStateToPropsParam, mapDispatchToProps: MapDispatchToPropsParam -): ComponentDecorator; +): InferableComponentEnhancerWithProps; export declare function connect( mapStateToProps: MapStateToPropsParam, mapDispatchToProps: null | undefined, mergeProps: MergeProps, -): ComponentMergeDecorator; +): InferableComponentEnhancerWithProps; export declare function connect( mapStateToProps: null | undefined, mapDispatchToProps: MapDispatchToPropsParam, mergeProps: MergeProps, -): ComponentMergeDecorator; +): InferableComponentEnhancerWithProps; export declare function connect( mapStateToProps: null | undefined, mapDispatchToProps: null | undefined, mergeProps: MergeProps, -): ComponentMergeDecorator; +): InferableComponentEnhancerWithProps; export declare function connect( mapStateToProps: MapStateToPropsParam, mapDispatchToProps: MapDispatchToPropsParam, mergeProps: MergeProps, -): ComponentMergeDecorator; +): InferableComponentEnhancerWithProps; export declare function connect( mapStateToProps: MapStateToPropsParam, mapDispatchToProps: null | undefined, mergeProps: null | undefined, - options: Options -): ComponentDecorator & TStateProps & TOwnProps>; + options: Options +): InferableComponentEnhancerWithProps & TStateProps, TOwnProps>; export declare function connect( mapStateToProps: null | undefined, mapDispatchToProps: MapDispatchToPropsParam, mergeProps: null | undefined, - options: Options -): ComponentDecorator; + options: Options +): InferableComponentEnhancerWithProps; export declare function connect( mapStateToProps: MapStateToPropsParam, mapDispatchToProps: MapDispatchToPropsParam, mergeProps: null | undefined, - options: Options -): ComponentDecorator; + options: Options +): InferableComponentEnhancerWithProps; export declare function connect( mapStateToProps: MapStateToPropsParam, mapDispatchToProps: MapDispatchToPropsParam, mergeProps: MergeProps, - options: Options -): ComponentMergeDecorator; + options: Options +): InferableComponentEnhancerWithProps; interface MapStateToProps { - (state: any, ownProps?: TOwnProps): TStateProps; + (state: any, ownProps: TOwnProps): TStateProps; } interface MapStateToPropsFactory { - (initialState: any, ownProps?: TOwnProps): MapStateToProps; + (initialState: any, ownProps: TOwnProps): MapStateToProps; } type MapStateToPropsParam = MapStateToProps | MapStateToPropsFactory; interface MapDispatchToPropsFunction { - (dispatch: Dispatch, ownProps?: TOwnProps): TDispatchProps; -} - -interface MapDispatchToPropsObject { - [name: string]: ActionCreator; + (dispatch: Dispatch, ownProps: TOwnProps): TDispatchProps; } type MapDispatchToProps = - MapDispatchToPropsFunction | MapDispatchToPropsObject; + MapDispatchToPropsFunction | TDispatchProps; interface MapDispatchToPropsFactory { - (dispatch: Dispatch, ownProps?: TOwnProps): MapDispatchToProps; + (dispatch: Dispatch, ownProps: TOwnProps): MapDispatchToProps; } type MapDispatchToPropsParam = MapDispatchToProps | MapDispatchToPropsFactory; @@ -131,7 +152,7 @@ interface MergeProps { (stateProps: TStateProps, dispatchProps: TDispatchProps, ownProps: TOwnProps): TMergedProps; } -interface Options { +interface Options extends ConnectOptions { /** * If true, implements shouldComponentUpdate and shallowly compares the result of mergeProps, * preventing unnecessary updates, assuming that the component is a “pure” component @@ -140,11 +161,106 @@ interface Options { * @default true */ pure?: boolean; + + /** + * When pure, compares incoming store state to its previous value. + * @default strictEqual + */ + areStatesEqual?: (nextState: any, prevState: any) => boolean; + + /** + * When pure, compares incoming props to its previous value. + * @default shallowEqual + */ + areOwnPropsEqual?: (nextOwnProps: TOwnProps, prevOwnProps: TOwnProps) => boolean; + + /** + * When pure, compares the result of mapStateToProps to its previous value. + * @default shallowEqual + */ + areStatePropsEqual?: (nextStateProps: TStateProps, prevStateProps: TStateProps) => boolean; + + /** + * When pure, compares the result of mergeProps to its previous value. + * @default shallowEqual + */ + areMergedPropsEqual?: (nextMergedProps: TMergedProps, prevMergedProps: TMergedProps) => boolean; +} + +/** + * Connects a React component to a Redux store. It is the base for {@link connect} but is less opinionated about + * how to combine state, props, and dispatch into your final props. It makes no + * assumptions about defaults or memoization of results, leaving those responsibilities to the caller.It does not + * modify the component class passed to it; instead, it returns a new, connected component class for you to use. + * + * @param selectorFactory The selector factory. See {@type SelectorFactory} for details. + * @param connectOptions If specified, further customizes the behavior of the connector. Additionally, any extra + * options will be passed through to your selectorFactory in the factoryOptions argument. + */ +export declare function connectAdvanced( + selectorFactory: SelectorFactory, + connectOptions?: ConnectOptions & TFactoryOptions +): AdvancedComponentDecorator; + +/** + * Initializes a selector function (during each instance's constructor). That selector function is called any time the + * connector component needs to compute new props, as a result of a store state change or receiving new props. The + * result of selector is expected to be a plain object, which is passed as the props to the wrapped + * component. If a consecutive call to selector returns the same object (===) as its previous + * call, the component will not be re-rendered. It's the responsibility of selector to return that + * previous object when appropriate. + */ +export interface SelectorFactory { + (dispatch: Dispatch, factoryOptions: TFactoryOptions): Selector +} + +export interface Selector { + (state: S, ownProps: TOwnProps): TProps +} + +export interface ConnectOptions { + /** + * Computes the connector component's displayName property relative to that of the wrapped component. Usually + * overridden by wrapper functions. + * + * @default name => 'ConnectAdvanced('+name+')' + * @param componentName + */ + getDisplayName?: (componentName: string) => string + /** + * Shown in error messages. Usually overridden by wrapper functions. + * + * @default 'connectAdvanced' + */ + methodName?: string + /** + * If defined, a property named this value will be added to the props passed to the wrapped component. Its value + * will be the number of times the component has been rendered, which can be useful for tracking down unnecessary + * re-renders. + * + * @default undefined + */ + renderCountProp?: string + /** + * Controls whether the connector component subscribes to redux store state changes. If set to false, it will only + * re-render on componentWillReceiveProps. + * + * @default true + */ + shouldHandleStateChanges?: boolean + /** + * The key of props/context to get the store. You probably only need this if you are in the inadvisable position of + * having multiple stores. + * + * @default 'store' + */ + storeKey?: string /** - * If true, stores a ref to the wrapped component instance and makes it available via - * getWrappedInstance() method. Defaults to false. + * If true, stores a ref to the wrapped component instance and makes it available via getWrappedInstance() method. + * + * @default false */ - withRef?: boolean; + withRef?: boolean } export interface ProviderProps {