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

Subscribe<T> type error #50

Closed
Magellol opened this issue Jul 22, 2019 · 4 comments
Closed

Subscribe<T> type error #50

Magellol opened this issue Jul 22, 2019 · 4 comments
Assignees

Comments

@Magellol
Copy link

The following type is throwing errors in TS and I think it's specifically the U | void passed to StateListener. TS cannot infer the shape of the state passed to the listener fn.

https://github.com/react-spring/zustand/blob/f936e3d9df0e3a18e2504e62828b23f6e9937fe3/src/index.ts#L23-L25

image

Seems like converting U | void into U fixes it but we have to explicitely pass the generic when called. I was wondering why we'd need void in this case.

export declare type Subscribe<T extends State> = <U>(
-  listener: StateListener<U | void>,
+  listener: StateListener<U>,
  options?: SubscribeOptions<T, U>
) => () => void;
const [, api] = create<State>(() => ({});
api.subscribe<State>(s => {}) // This passes type checking

I have created a reproducible example: https://codesandbox.io/s/adoring-brook-264gb

@JeremyRH JeremyRH self-assigned this Aug 20, 2019
@JeremyRH
Copy link
Contributor

Unfortunately, I think your listener can be called with no parameters when there is an error in your selector, equality function, or listener.
https://github.com/react-spring/zustand/blob/2c8908e07259d948592190471f00255a55af627b/src/index.ts#L81

Maybe there is a better way to express the type or maybe there is a better way to handle when errors happen.

@Magellol
Copy link
Author

Gotcha. I wasn't sure the listener could be called with no arguments.

I'm guessing we can workaround it by checking if State is passed in first then apply any logic. The following sandbox shows how: https://codesandbox.io/s/xenodochial-silence-z7obf

I've tried using function overloads but maybe I'm not grasping them entirely. I've felt maybe using a node-like signature would be good but TS doesn't seem to recognize my two different inputs.

function listen<S>(error: Error, state: undefined): void
function listen<S>(error: null, state: S): void
function listen(error, state) {
    if (error) {
        error; // This is of type `any`
    }
}

@JeremyRH
Copy link
Contributor

I like the error first callback pattern but I don't think it's very common for listeners. Still, it might be a good solution. The reason we call the listener after an error is to avoid zombie components that silently freeze after an error happens. I think the error first callback pattern is good for this but I'll need to do some testing to be sure.

Thank you for your help with this.

@JeremyRH
Copy link
Contributor

JeremyRH commented Oct 9, 2019

@Magellol I think the listener should accept an error as a second argument to make the change more compatible with previous versions. This proposed change uses the function signatures:

export interface StateListener<T> {
  (state: T): void
  (state: null, error: Error): void
}

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