import { ComponentType, NamedExoticComponent } from 'react' import { Observable } from 'rxjs' import hoistNonReactStatics = require('hoist-non-react-statics') interface ObservableConvertible { readonly observe: () => Observable } type ExtractObservableType = T extends Observable ? U : T extends ObservableConvertible ? U : T export type ExtractedObservables = { [K in keyof T]: ExtractObservableType } /** * A property P will be present if: * - it is present in DecorationTargetProps * * Its value will be dependent on the following conditions * - if property P is present in InjectedProps and its definition extends the definition * in DecorationTargetProps, then its definition will be that of DecorationTargetProps[P] * - if property P is not present in InjectedProps then its definition will be that of * DecorationTargetProps[P] * - if property P is present in InjectedProps but does not extend the * DecorationTargetProps[P] definition, its definition will be that of InjectedProps[P] */ type Matching = { [P in keyof DecorationTargetProps]: P extends keyof InjectedProps ? InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : InjectedProps[P] : DecorationTargetProps[P] } // Infers prop type from component C type GetProps = C extends ComponentType ? P : never /** * a property P will be present if : * - it is present in both DecorationTargetProps and InjectedProps * - InjectedProps[P] can satisfy DecorationTargetProps[P] * ie: decorated component can accept more types than decorator is injecting * * For decoration, inject props or ownProps are all optionally * required by the decorated (right hand side) component. * But any property required by the decorated component must be satisfied by the injected property. */ type Shared = { [P in Extract< keyof InjectedProps, keyof DecorationTargetProps >]?: InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : never } // Applies LibraryManagedAttributes (proper handling of defaultProps // and propTypes), as well as defines WrappedComponent. type ConnectedComponent, P> = NamedExoticComponent< JSX.LibraryManagedAttributes > & hoistNonReactStatics.NonReactStatics & { WrappedComponent: C } // 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. type InferableComponentEnhancer = < C extends ComponentType>>, >( component: C, ) => ConnectedComponent< C, Omit, keyof Shared>> & TNeedsProps > type Omit = Pick> type ObservableifyProps = { [K in keyof Pick]: Observable } & { [K in keyof Pick]: ObservableConvertible } & Omit export default function withObservables( triggerProps: Array, getObservables: (props: InputProps) => ObservableProps, ): InferableComponentEnhancer, InputProps>