Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

recompose inference broken by #30856 #30942

Closed
sandersn opened this issue Apr 15, 2019 · 6 comments · Fixed by #30963
Closed

recompose inference broken by #30856 #30942

sandersn opened this issue Apr 15, 2019 · 6 comments · Fixed by #30963
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@sandersn
Copy link
Member

sandersn commented Apr 15, 2019

Caused by the inference change in #30856. Repros in types/recompose/recompose-tests.tsx on line 183. Below is a standalone repro.

Code

type Factory<U> = () => { [P in keyof U]: (props: number) => U[P]; }
declare function withH<U>(fact: Factory<U>): U
const enhancer4: { onChange: (e: any) => void } =
    withH(() => ({ onChange: (props) => (e: any) => {} }));

Expected behavior:

enhancer4: { onChange: (e: any) => void }

Actual behavior:

enhancer4: { onChange: unknown }
@sandersn sandersn added the Bug A bug in TypeScript label Apr 15, 2019
@sandersn
Copy link
Member Author

Somewhat smaller:

namespace fff {
declare function withH<T, U>(handlerCreators: HandleCreatorsFactory<T, U>): InferableComponentEnhancerWithProps<U & T, T>;
    type Props = { out: number }
    type HandleCreatorsHandlers<T, U> = {
        [P in keyof U]: (props: T) => U[P];
    };
    type HandleCreatorsFactory<TOutter, THandlers> = (initialProps: TOutter) =>
        HandleCreatorsHandlers<TOutter, THandlers>;
    interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
        <P extends TInjectedProps>(
            component: React.ComponentType<P>
        ): React.ComponentClass<Omit<P, keyof TInjectedProps> & TNeedsProps>
    }

    function fffff() {
        const enhancer4 = withH((props: Props) => ({
            onChange: (props) => (e: any) => {},
            onSubmit: (props) => (e: React.MouseEvent<any>) => {},
        }));
        const Enhanced4 = enhancer4(({onChange, onSubmit, out}) => <div onClick={onSubmit}>{out}</div>);
    }
}

@sandersn sandersn reopened this Apr 15, 2019
@weswigham
Copy link
Member

@sandersn are the react references in these examples required?

@sandersn
Copy link
Member Author

As far as I had a chance to tell. They might not ultimately be.

@sandersn
Copy link
Member Author

sandersn commented Apr 16, 2019

Here is a standalone repro. I'll cut it down more if I have time, but there are no more react references:

namespace fff {
declare function withH<T, U>(handlerCreators: HandleCreatorsFactory<T, U>): InferableComponentEnhancerWithProps<U & T, T>;
    interface StaticLifecycle<P, S> {
        getDerivedStateFromProps?: GetDerivedStateFromProps<P, S>;
        getDerivedStateFromError?: GetDerivedStateFromError<P, S>;
    }
    type GetDerivedStateFromProps<P, S> = (nextProps: Readonly<P>, prevState: S) => Partial<S> | null;
    type GetDerivedStateFromError<P, S> = (error: any) => Partial<S> | null;

    interface FunctionComponent<P = {}> {
        (props: P, context?: any): ReactElement | null;
        propTypes?: WeakValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        defaultProps?: Partial<P>;
        displayName?: string;
    }

    type JSXElementConstructor<P> = (props: P) => ReactElement | null
    interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
        type: T;
        props: P;
        key: string | number | null;
    }

    declare const nominalTypeHack: unique symbol;
    interface Validator<T> {
        (props: object, propName: string, componentName: string, location: string, propFullName: string): Error | null;
        [nominalTypeHack]?: T;
    }

    type ValidationMap<T> = { [K in keyof T]?: Validator<T[K]> };
    type WeakValidationMap<T> = {
        [K in keyof T]?: null extends T[K]
            ? Validator<T[K] | null | undefined>
            : undefined extends T[K]
            ? Validator<T[K] | null | undefined>
            : Validator<T[K]>
    };



    type Props = { out: number }
    type HandleCreatorsHandlers<T, U> = {
        [P in keyof U]: (props: T) => U[P];
    };
    type HandleCreatorsFactory<T, U> = (initialProps: T) => HandleCreatorsHandlers<T, U>;
    interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
        <P extends TInjectedProps>(
            component: FunctionComponent<P>
        ): StaticLifecycle<Omit<P, keyof TInjectedProps> & TNeedsProps, any>
    }

    function fffff() {
        const enhancer4 = withH((props: Props) => ({
            onChange: (props) => (e: any) => {},
            onSubmit: (props) => (e: React.MouseEvent<any>) => {},
        }));
        const Enhanced4 = enhancer4(({onChange, onSubmit, out}) => <div onClick={onSubmit}>{out}</div>);
    }
}

Edit: much smaller version:

namespace fff {
declare function withH<T, U>(handlerCreators: HandleCreatorsFactory<T, U>): InferableComponentEnhancerWithProps<U & T, T>;
    interface FunctionComponent<P = {}> {
        (props: P, context?: any): ReactElement | null;
    }
    interface ReactElement {
        key: string | number | null;
    }

    type Props = { out: number }
    type HandleCreatorsHandlers<T, U> = {
        [P in keyof U]: (props: T) => U[P];
    };
    type HandleCreatorsFactory<T, U> = (initialProps: T) => HandleCreatorsHandlers<T, U>;
    interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
        <P extends TInjectedProps>(
            component: FunctionComponent<P>
        ): Omit<P, keyof TInjectedProps> & TNeedsProps
    }

    function fffff() {
        const enhancer4 = withH((props: Props) => ({
            onChange: (props) => (e: any) => {},
            onSubmit: (props) => (e: React.MouseEvent<any>) => {},
        }));
        const Enhanced4 = enhancer4(({onChange, onSubmit, out}) => <div onClick={onSubmit}>{out}</div>);
    }
}

@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.5.0 milestone Apr 16, 2019
@ahejlsberg
Copy link
Member

I have a fix, will put up shortly. Trivial matter of properly propagating ObjectFlags.NonInferrableType through object literal widening.

@ahejlsberg
Copy link
Member

Much smaller repro:

declare function withH<T, U>(handlerCreators: HandleCreatorsFactory<T, U>): U;

type Props = { out: number }

type HandleCreatorsFactory<T, U> = (initialProps: T) => { [P in keyof U]: (props: T) => U[P] };

const enhancer4 = withH((props: Props) => ({
    onChange: (props) => (e: any) => {},
    onSubmit: (props) => (e: React.MouseEvent<any>) => {},
}));

enhancer4.onChange(null);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants