Skip to content

Contextual typing not working on optional members #16817

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

Closed
DanielRosenwasser opened this issue Jun 29, 2017 · 4 comments
Closed

Contextual typing not working on optional members #16817

DanielRosenwasser opened this issue Jun 29, 2017 · 4 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jun 29, 2017

The following sample works perfectly in noImplicitAny unless I turn on strictNullChecks. In that event, the s parameter on foo seems to get an implicit any.

interface ActionsObject<State> {
    [prop: string]: (state: State) => State
}

interface Options<State, Actions> {
    state?: State;
    view?: (state: State, actions: Actions) => any;
    actions?: Actions;
}

declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;

app({
    state: 100,
    actions: {
        foo: s => s
    },
    view: (s, a) => null as any,
})

If I make actions non-optional, then things work perfectly again.

Context: jorgebucaran/hyperapp#87

@weswigham
Copy link
Member

weswigham commented Aug 7, 2017

This isn't strictNullChecks specific (though it's much easier to trigger the bug there); we're failing to produce parameter inferences for any parameter within an index within a union:

    interface ActionsObject<State> {
        [prop: string]: (state: State) => State;
    }
    
    interface Options<State, Actions> {
        state?: State;
        view?: (state: State, actions: Actions) => any;
        actions: string | Actions;
    }
    
    declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;
    
    app({
        state: 100,
        actions: {
            foo: s => s
                 ~
!!! error TS7006: Parameter 's' implicitly has an 'any' type.
        },
        view: (s, a) => undefined as any,
    });

@ahejlsberg
Copy link
Member

This is working as intended. The actions property of the object literal is contextually typed by a type parameter type Actions, and during the inference process we don't yet know what the type argument for Actions will be (and constraints don't factor into type inference). I suggest rewriting the example to:

interface ActionsObject<State> {
    [prop: string]: (state: State) => State
}

interface Options<State> {
    state?: State;
    actions?: ActionsObject<State>;
}

declare function app<State>(obj: Options<State>): void;

app({
    state: 100,
    actions: {
        foo: s => s
    },
})

If it is really important to capture the actual type of the object literal, it can be done with an intersection type:

interface ActionsObject<State> {
    [prop: string]: (state: State) => State
}

interface Options<State> {
    state?: State;
    actions?: ActionsObject<State>;
}

declare function app<State, Actions>(obj: Options<State> & Actions): void;

app({
    state: 100,
    actions: {
        foo: s => s
    },
})

(I omitted the view property to reduce noise.)

@ahejlsberg ahejlsberg added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Bug A bug in TypeScript labels Oct 20, 2017
@ahejlsberg ahejlsberg removed this from the TypeScript 2.7 milestone Oct 20, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Nov 6, 2017

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@mhegazy mhegazy closed this as completed Nov 6, 2017
@weswigham weswigham reopened this Nov 6, 2017
@weswigham
Copy link
Member

weswigham commented Nov 6, 2017

This issue is tagged incorrectly and we have a fix up, it's pretty simple. We actually do contextually type the nonunion case right (the generic's constraint is the contextual type), so it is, in fact, a bug that a union is not appropriately contextually typed. The issue is that when we get the apparent type of a contextual type, we didn't get the apparent type of all union members - we just returned the union.

@weswigham weswigham added Bug A bug in TypeScript and removed Working as Intended The behavior described is the intended behavior; this is not a bug labels Nov 6, 2017
@weswigham weswigham self-assigned this Nov 6, 2017
@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Nov 6, 2017
@mhegazy mhegazy added this to the TypeScript 2.7 milestone Nov 6, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

5 participants