Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript type not working with spread operator

I have a redux style reducer (I'm using ngrx) that is returning a specific type. When I use the spread operator in my return object, the typescript linter is not catching invalid properties.

Here is my interface:

interface MyState {
    firstName: string;
    lastName: string;
    age: number;
}

Here is my reducer. Action is an ngrx action:

function reducer(state = initialState, action: Action): MyState {
    switch (action.type) {
        case Actions.COOL_ACTION:
            return {
                ...state,
                propertyDoesNotExist: action.payload, // <- no error
            };
        default:
            return state;
    }
}

I would expect that propertyDoesNotExist would be flagged, but it is not. I've tried casting <CalendarState> the return object, the state property ...(<CalendarState>state) and using the as alias, but it doesn't help.

It's like the spread operator messes up the types.

like image 677
Richard.Davenport Avatar asked Oct 17 '22 11:10

Richard.Davenport


1 Answers

By using ...state, the returned expression is no longer an object literal, and TypeScript won't complain if it's a subtype of the return type (i.e. has extra properties). I ran into this problem myself and wrote a little helper function to signal extra properties:

const applyChanges = <S, K extends keyof S>(state : S, changes : Pick<S, K>) : S =>
  Object.assign({}, state, changes);

(using Object.assign instead of spread operators because of this issue: https://github.com/Microsoft/TypeScript/issues/14409)

To use applyChanges, simply replace

return {...state,
    propertyDoesNotExist: action.payload, // <- no error
};

with

return applyChanges(state, {
    propertyDoesNotExist: action.payload, // <- error
});
like image 55
Oblosys Avatar answered Oct 21 '22 09:10

Oblosys