Skip to content

Object spread allows unknown properties as function return values #17422

Closed

Description

TypeScript Version: 2.4.2

Code

interface IState {
    foo: number;
}
export const fun: (state: IState) => IState = (state) => {
    return { ...state, bar: 2 };
};

Expected behavior:

Object literal may only specify known properties, and 'bar' does not exist in type 'IState'.

Actual behavior:

No error

Activity

  1. Jessidhia commented on Jul 26, 2017

    @Jessidhia

    It seems that spread completely disables excess property checking, and that that was an intentional decision.

    Though I'm not sure what would be the harm in checking for excess properties for the non-spread (directly specified) keys, especially when there is a contextual type 😕

  2. thasner commented on Jul 26, 2017

    @thasner
    Author

    It seems that spread completely disables excess property checking, and that that was an intentional decision.

    @Kovensky that's not true.

    On TS 2.4.2 this code sample emits the appropriate error:

    Object literal may only specify known properties, and 'bar' does not exist in type 'IState'.

    interface IState {
        foo: number;
    }
    export const fun: (state: IState) => IState = (state) => {
        const ret: IState = { ...state, bar: 2 }; // TS throws error here
        return ret;
    };
    
  3. aluanhaddad commented on Jul 27, 2017

    @aluanhaddad
    Contributor

    Thomas Hasner (@thasner) your example is actually not the same as that in the OP though.

    The OP example is

    interface IState {
        foo: number;
    }
    export const fun: (state: IState) => IState = (state) => {
        return { ...state, bar: 2 };
    };

    such that the excess property, bar, is accepted as part of the inferred return type of the function variable's value

    In your example

    export const fun: (state: IState) => IState = (state) => {
        const ret: IState = { ...state, bar: 2 }; // TS throws error here
        return ret;
    };

    an intermediate, explicitly typed variable is introduced and that is where the error is triggered.
    It is as if you had written

    export const fun: (state: IState) => IState = (state): IState => {
      return {...state, bar: 2};
    };

    where the explicit return type annotation on the value that implements fun is what triggers the error.

    FWIW, I think the current behavior is just fine. Anything more rigid would wreck many simple and practical factory patterns and make perfectly reasonable and sound assignability relationships illegal.

  4. mhegazy commented on Sep 6, 2017

    @mhegazy
    Contributor

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

  5. olee commented on May 30, 2018

    @olee

    I would like to ask why this was closed.
    The example presented above shows how weird this behaves with

    export const fun: (state: IState) => IState = (state) => {
        const ret: IState = { ...state, bar: 2 }; // TS throws error here
        return ret;
    };

    correctly throwing an error, but

    export const fun: (state: IState) => IState = (state): IState => {
      return {...state, bar: 2};
    };

    compiling just fine.
    I was gonna write code a lot like the second example (for redux reducers) but it won't properly typecheck like that so I would have to introduce intermediary variables which will appear in the output as well which I'd rather avoid.

  6. RyanCavanaugh commented on May 31, 2018

    @RyanCavanaugh
    Member
  7. locked and limited conversation to collaborators on Jul 31, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions