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

Interfere return type in .case doesn't work #20

Open
MistyKuu opened this issue Sep 21, 2017 · 1 comment
Open

Interfere return type in .case doesn't work #20

MistyKuu opened this issue Sep 21, 2017 · 1 comment

Comments

@MistyKuu
Copy link

Hey, I just tried to use the library but unfortunately there is no type check in .case method for return value.

If I implement my reducer like this:

const execute = (execute: Execute, action: ReduxAction): Execute => {
  if (isType(action, actions.calibrationSettingsDone)) return { ...execute, settings: action.payload.result };
...

it will not allow me to return settings1 property because it doesn't exist in Execute model and it's fine however in your library it's completely fine to return any object.

@dphilipson
Copy link
Owner

dphilipson commented Sep 21, 2017

You're right. Unfortunately this is a limitation of TypeScript that I don't see a way to work around in this circumstance. Unknown properties on object literals are not checked in the return value of functions which are given as arguments because TypeScript thinks its fine to accept a function which produces a return type which is a subtype of the desired type. A demo of this behavior:

interface HasX {
    x: number;
}

function getHasX(): HasX {
    // Extra properties are errors.
    return { x: 1, y: 2 };
}

function callGetHasX(f: () => HasX) {
    f();
}

// No error here.
callGetHasX(() => ({ x: 1, y: 2 }));

If this failure is enough of problem for you to not use this library, I understand. However, here are a few workarounds:

  1. Use a variant of object spread from a library with a more restrictive signature.

    In my code, I use pureAssign from the pure-assign library instead of object spread or Object.assign. Its signature of

     function pureAssign<T>(baseObject: T, ...updates: Array<Partial<T>>): T

    prevents unknown properties from being added which are not in the base type. Not to say you have to use this library in particular, but using a version of object assignment with more restrictive typing can be a generally good idea when writing TypeScript code.

  2. Pull your handlers out into separate functions.

    You can choose to write your code as

    const reducer = reducerWithoutInitialState<Execute>()
        .case(actions.calibrationSettingsDone, handleCalibrationSettingsDone);
    
    const handleCalibrationSettingsDone =
        (execute: Execute, payload: CalibrationSettingsDonePayload): Execute => 
            ({ ...execute, settings:  payload.result });

    Some people prefer this style anyways because it makes the reducer look less cluttered, although it does require more typing.

I'll think about this for a bit to see if I can come up with a better solution. If not, I'll make a note in the README about this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants