mirror of
https://github.com/gosticks/DefinitelyTyped.git
synced 2026-06-28 22:30:01 +00:00
[react-redux]: decoration target props should not extend injected props, contravariance issue (#25228)
* feat(react-redux): add strict null check * feat(react-redux): remove improper inference tests Infered decorated typings would rely on the implicit and false assumption that decorated component props extend injected props and own props #24922 #24913 * fix(react-redux): injected props and decorated component props intersection * InjectedProps should not have to extend DecoratedProps * DecoratedProps should not have to extend InjectedProps * DecoratedProps should extend Intersection<InjectedProps, DecoratedProps> * Remaining Props should be required on the decoration output #24913 #24922 * feat(react-redux): add new commiters * feat(react-redux): 2.9 keyof compatibility depends on Extract (2.8)
This commit is contained in:
committed by
Sheetal Nandi
parent
d12082bfdf
commit
65b176eaef
2
types/mirrorx/index.d.ts
vendored
2
types/mirrorx/index.d.ts
vendored
@@ -2,7 +2,7 @@
|
||||
// Project: https://github.com/mirrorjs/mirror
|
||||
// Definitions by: Aaronphy <https://github.com/aaronphy>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import * as H from 'history';
|
||||
|
||||
|
||||
2
types/next-redux-wrapper/index.d.ts
vendored
2
types/next-redux-wrapper/index.d.ts
vendored
@@ -2,7 +2,7 @@
|
||||
// Project: https://github.com/kirill-konshin/next-redux-wrapper
|
||||
// Definitions by: Steve <https://github.com/stevegeek>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
/// <reference types="node" />
|
||||
/*~ Note that ES6 modules cannot directly export callable functions.
|
||||
|
||||
2
types/react-intl-redux/index.d.ts
vendored
2
types/react-intl-redux/index.d.ts
vendored
@@ -2,7 +2,7 @@
|
||||
// Project: https://github.com/ratson/react-intl-redux
|
||||
// Definitions by: Karol Janyst <https://github.com/LKay>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import { Action } from "redux"
|
||||
import { Provider as ReduxProvider } from "react-redux"
|
||||
|
||||
2
types/react-redux-toastr/index.d.ts
vendored
2
types/react-redux-toastr/index.d.ts
vendored
@@ -4,7 +4,7 @@
|
||||
// Artyom Stukans <https://github.com/artyomsv>
|
||||
// Mika Kuitunen <https://github.com/kulmajaba>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import { Component } from 'react';
|
||||
import { Action, ActionCreator, Reducer } from 'redux';
|
||||
|
||||
29
types/react-redux/index.d.ts
vendored
29
types/react-redux/index.d.ts
vendored
@@ -8,8 +8,11 @@
|
||||
// Nicholas Boll <https://github.com/nicholasboll>
|
||||
// Dibyo Majumdar <https://github.com/mdibyo>
|
||||
// Prashant Deva <https://github.com/pdeva>
|
||||
// Thomas Charlat <https://github.com/kallikrein>
|
||||
// Valentin Descamps <https://github.com/val1984>
|
||||
// Johann Rakotoharisoa <https://github.com/jrakotoharisoa>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
// Known Issue:
|
||||
// There is a known issue in TypeScript, which doesn't allow decorators to change the signature of the classes
|
||||
@@ -37,21 +40,39 @@ type ActionCreator<A> = Redux.ActionCreator<A>;
|
||||
// Diff / Omit taken from https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-311923766
|
||||
type Omit<T, K extends keyof T> = Pick<T, ({ [P in keyof T]: P } & { [P in K]: never } & { [x: string]: never, [x: number]: never })[keyof T]>;
|
||||
|
||||
|
||||
export interface DispatchProp<A extends Redux.Action = Redux.AnyAction> {
|
||||
dispatch?: Dispatch<A>;
|
||||
dispatch: Dispatch<A>;
|
||||
}
|
||||
|
||||
interface AdvancedComponentDecorator<TProps, TOwnProps> {
|
||||
(component: Component<TProps>): ComponentClass<TOwnProps>;
|
||||
}
|
||||
|
||||
/**
|
||||
* a property P will be present if :
|
||||
* - it is present in both DecorationTargetProps and InjectedProps
|
||||
* - DecorationTargetProps[P] extends InjectedProps[P]
|
||||
* ie: decorated component can accept more types than decorator is injecting
|
||||
*
|
||||
* For decoration, inject props or ownProps are all optionnaly
|
||||
* required by the decorated (right hand side) component.
|
||||
* But any property required by the decorated component must extend the injected property
|
||||
*/
|
||||
type Shared<
|
||||
InjectedProps,
|
||||
DecorationTargetProps extends Shared<InjectedProps, DecorationTargetProps>
|
||||
> = {
|
||||
[P in Extract<keyof InjectedProps, keyof DecorationTargetProps>]?: DecorationTargetProps[P] extends InjectedProps[P] ? InjectedProps[P] : never;
|
||||
};
|
||||
|
||||
// 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<TInjectedProps, TNeedsProps> {
|
||||
<P extends TInjectedProps>(
|
||||
<P extends Shared<TInjectedProps, P>>(
|
||||
component: Component<P>
|
||||
): ComponentClass<Omit<P, keyof TInjectedProps> & TNeedsProps> & {WrappedComponent: Component<P>}
|
||||
): ComponentClass<Omit<P, keyof Shared<TInjectedProps, P>> & TNeedsProps> & {WrappedComponent: Component<P>}
|
||||
}
|
||||
|
||||
// Injects props and removes them from the prop requirements.
|
||||
|
||||
@@ -349,8 +349,7 @@ connect<ICounterStateProps, ICounterDispatchProps, {}, ICounterStateProps & ICou
|
||||
|
||||
|
||||
class App extends Component<any, any> {
|
||||
render(): JSX.Element {
|
||||
// ...
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -544,14 +543,12 @@ interface TestState {
|
||||
class TestComponent extends Component<TestProp & DispatchProp, TestState> { }
|
||||
const WrappedTestComponent = connect()(TestComponent);
|
||||
|
||||
// return value of the connect()(TestComponent) is of the type TestComponent
|
||||
let ATestComponent: React.ComponentClass<TestProp> = null;
|
||||
ATestComponent = TestComponent;
|
||||
ATestComponent = WrappedTestComponent;
|
||||
|
||||
let anElement: ReactElement<TestProp>;
|
||||
// return value of the connect()(TestComponent) is assignable to a ComponentClass<TestProp>
|
||||
// ie: DispatchProp has been removed through decoration
|
||||
const ADecoratedTestComponent: React.ComponentClass<TestProp> = WrappedTestComponent;
|
||||
<WrappedTestComponent property1={42} />;
|
||||
<ATestComponent property1={42} />;
|
||||
|
||||
const ATestComponent: React.ComponentClass<TestProp> = TestComponent; // $ExpectError
|
||||
|
||||
class NonComponent {}
|
||||
// this doesn't compile
|
||||
@@ -748,7 +745,8 @@ namespace Issue15463 {
|
||||
interface ISpinnerProps{
|
||||
showGlobalSpinner: boolean;
|
||||
}
|
||||
class SpinnerClass extends React.Component<ISpinnerProps & DispatchProp, undefined> {
|
||||
|
||||
class SpinnerClass extends React.Component<ISpinnerProps & DispatchProp> {
|
||||
render() {
|
||||
return (<div />);
|
||||
}
|
||||
@@ -815,7 +813,7 @@ namespace TestControlledComponentWithoutDispatchProp {
|
||||
}
|
||||
|
||||
namespace TestDispatchToPropsAsObject {
|
||||
const onClick: ActionCreator<{}> = null;
|
||||
const onClick: ActionCreator<{}> = () => ({});
|
||||
const mapStateToProps = (state: any) => {
|
||||
return {
|
||||
title: state.app.title as string,
|
||||
@@ -900,24 +898,71 @@ namespace TestCreateProvider {
|
||||
ReactDOM.render(<Combined />, document.body);
|
||||
}
|
||||
|
||||
namespace TestTypeInference {
|
||||
interface State { a: number };
|
||||
namespace TestWithoutTOwnPropsDecoratedInference {
|
||||
|
||||
const OnlyState = connect(
|
||||
(state: {a: number}, props: {b: number}) => ({a: state.a, c: state.a + props.b})
|
||||
)(props => <span>{props.a} + {props.b} = {props.c}</span>)
|
||||
interface State { a: number };
|
||||
ReactDOM.render(<OnlyState b={1} />, document.body);
|
||||
interface ForwardedProps {
|
||||
forwarded: string;
|
||||
}
|
||||
|
||||
const OnlyDispatch = connect(
|
||||
undefined,
|
||||
(dispatch, props: {b: number}) => ({action: () => dispatch({type: 'action', b: props.b})})
|
||||
)(props => <span onClick={props.action}>{props.b}</span>)
|
||||
ReactDOM.render(<OnlyDispatch b={1} />, document.body);
|
||||
interface OwnProps {
|
||||
own: string;
|
||||
}
|
||||
|
||||
const StateAndDispatch = connect(
|
||||
(state: {a: number}, props: {b: number}) => ({a: state.a, c: state.a + props.b}),
|
||||
(dispatch, props: {b: number}) => ({action: () => dispatch({type: 'action', b: props.b})})
|
||||
)(props => <span>{props.a} + {props.b} = {props.c}</span>)
|
||||
ReactDOM.render(<StateAndDispatch b={1} />, document.body);
|
||||
interface StateProps {
|
||||
state: string;
|
||||
}
|
||||
|
||||
class WithoutOwnPropsComponentClass extends React.Component<ForwardedProps & StateProps & DispatchProp<any>> {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
const WithoutOwnPropsComponentStateless: React.StatelessComponent<ForwardedProps & StateProps & DispatchProp<any>> = () => (<div />);
|
||||
|
||||
function mapStateToProps4(state: any, ownProps: OwnProps): StateProps {
|
||||
return { state: 'string' };
|
||||
}
|
||||
|
||||
// these decorations should compile, it is perfectly acceptable to receive props and ignore them
|
||||
const ConnectedWithOwnPropsClass = connect(mapStateToProps4)(WithoutOwnPropsComponentClass);
|
||||
const ConnectedWithOwnPropsStateless = connect(mapStateToProps4)(WithoutOwnPropsComponentStateless);
|
||||
const ConnectedWithTypeHintClass = connect<StateProps, void, OwnProps>(mapStateToProps4)(WithoutOwnPropsComponentClass);
|
||||
const ConnectedWithTypeHintStateless = connect<StateProps, void, OwnProps>(mapStateToProps4)(WithoutOwnPropsComponentStateless);
|
||||
|
||||
// This should compile
|
||||
React.createElement(ConnectedWithOwnPropsClass, { own: 'string', forwarded: 'string' });
|
||||
React.createElement(ConnectedWithOwnPropsClass, { own: 'string', forwarded: 'string' });
|
||||
|
||||
// This should not compile, it is missing ForwardedProps
|
||||
React.createElement(ConnectedWithOwnPropsClass, { own: 'string' }); // $ExpectError
|
||||
React.createElement(ConnectedWithOwnPropsStateless, { own: 'string' }); // $ExpectError
|
||||
|
||||
// This should compile
|
||||
React.createElement(ConnectedWithOwnPropsClass, { own: 'string', forwarded: 'string' });
|
||||
React.createElement(ConnectedWithOwnPropsStateless, { own: 'string', forwarded: 'string' });
|
||||
|
||||
// This should not compile, it is missing ForwardedProps
|
||||
React.createElement(ConnectedWithTypeHintClass, { own: 'string' }); // $ExpectError
|
||||
React.createElement(ConnectedWithTypeHintStateless, { own: 'string' }); // $ExpectError
|
||||
|
||||
interface AllProps {
|
||||
own: string
|
||||
state: string
|
||||
}
|
||||
|
||||
class AllPropsComponent extends React.Component<AllProps & DispatchProp<any>> {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
type PickedOwnProps = Pick<AllProps, "own">
|
||||
type PickedStateProps = Pick<AllProps, "state">
|
||||
|
||||
const mapStateToPropsForPicked: MapStateToProps<PickedStateProps, PickedOwnProps, {}> = (state: any): PickedStateProps => {
|
||||
return { state: "string" }
|
||||
}
|
||||
const ConnectedWithPickedOwnProps = connect(mapStateToPropsForPicked)(AllPropsComponent);
|
||||
<ConnectedWithPickedOwnProps own="blah" />
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
],
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"strictNullChecks": false,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"baseUrl": "../",
|
||||
"jsx": "react",
|
||||
|
||||
2
types/react-router-redux/index.d.ts
vendored
2
types/react-router-redux/index.d.ts
vendored
@@ -4,7 +4,7 @@
|
||||
// Shoya Tanaka <https://github.com/8398a7>
|
||||
// Mykolas <https://github.com/mykolas>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import {
|
||||
Store,
|
||||
|
||||
2
types/redux-auth-wrapper/index.d.ts
vendored
2
types/redux-auth-wrapper/index.d.ts
vendored
@@ -2,7 +2,7 @@
|
||||
// Project: https://github.com/mjrussell/redux-auth-wrapper
|
||||
// Definitions by: Karol Janyst <https://github.com/LKay>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import { ComponentClass, StatelessComponent, ComponentType, ReactType } from "react";
|
||||
|
||||
|
||||
2
types/redux-devtools/index.d.ts
vendored
2
types/redux-devtools/index.d.ts
vendored
@@ -2,7 +2,7 @@
|
||||
// Project: https://github.com/gaearon/redux-devtools
|
||||
// Definitions by: Petryshyn Sergii <https://github.com/mc-petry>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import * as React from 'react';
|
||||
import { GenericStoreEnhancer } from 'redux';
|
||||
|
||||
2
types/redux-form/v6/index.d.ts
vendored
2
types/redux-form/v6/index.d.ts
vendored
@@ -2,7 +2,7 @@
|
||||
// Project: https://github.com/erikras/redux-form
|
||||
// Definitions by: Carson Full <https://github.com/carsonf>, Daniel Lytkin <https://github.com/aikoven>, Karol Janyst <https://github.com/LKay>, Luka Zakrajsek <https://github.com/bancek>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import {
|
||||
ComponentClass,
|
||||
|
||||
@@ -127,7 +127,7 @@ const ConnectedDecoratedInitializeFromStateFormFunction = connect(
|
||||
|
||||
// React ComponentClass instead of StatelessComponent
|
||||
|
||||
class InitializeFromStateFormClass extends React.Component<Props & DispatchProp<any>> {
|
||||
class InitializeFromStateFormClass extends React.Component<Props> {
|
||||
render() {
|
||||
return InitializeFromStateFormFunction(this.props);
|
||||
}
|
||||
|
||||
2
types/redux-little-router/index.d.ts
vendored
2
types/redux-little-router/index.d.ts
vendored
@@ -2,7 +2,7 @@
|
||||
// Project: https://github.com/FormidableLabs/redux-little-router
|
||||
// Definitions by: priecint <https://github.com/priecint>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.6
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import * as React from "react";
|
||||
import { Reducer, Middleware, StoreEnhancer } from "redux";
|
||||
|
||||
Reference in New Issue
Block a user