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

fix(context): revert removing overloads in UseContextStore #817

Merged
merged 3 commits into from
Feb 28, 2022

Conversation

devanshj
Copy link
Contributor

Fixes #812

@codesandbox-ci
Copy link

codesandbox-ci bot commented Feb 20, 2022

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 52cc3f4:

Sandbox Source
React Configuration
React Typescript Configuration
React Browserify Configuration
React Snowpack Configuration
React Parcel Configuration
Next.js Configuration
modern-thunder-kzw5m8 Issue #812

Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @devanshj !
Would you be able to add types test to detect this for the future?

const { useStore } = createContext<CounterState>()
function _Counter() {
let x = useStore(useCallback((state) => state.count, []))
expectAreTypesEqual<typeof x, number>().toBe(true)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to use this because writing x: number = useStore(..) won't test the inference as even when the inference fails x will be inferred as any and it'll still compile.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Nice to learn a new technique.

Just wondered why it was inferred as any instead of unknown before this PR...

Copy link
Contributor Author

@devanshj devanshj Apr 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondered why it was inferred as any instead of unknown before this PR...

Oops sorry I missed this. It's because in useCallback's callback's constraint is (...a: any[]) => any as you can see here1. Hence here you will see x is any in useFoov37. But the correct constraint is for an unknown function is (...a: never[]) => unknown, in that case the code wouldn't even compile. (I hope this is what you were asking)

Footnotes

  1. They have changed it to Function in react 18 but that still is not correct.

Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@GuskiS
Copy link

GuskiS commented Feb 23, 2022

Can we get this merged and published?

@dai-shi
Copy link
Member

dai-shi commented Feb 23, 2022

I have one or two more PRs to work on to make a release. Meanwhile, you can keep using the codesandbox build.

@dai-shi dai-shi added this to the v3.7.1 milestone Feb 24, 2022
@dai-shi dai-shi merged commit 52cc17a into pmndrs:main Feb 28, 2022
Comment on lines +14 to +17
export type UseContextStore<T extends State> = {
(): T
<U>(selector: StateSelector<T, U>, equalityFn?: EqualityChecker<U>): U
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dai-shi Shouldn't this have been the following?

export type UseContextStore<T extends State> = {
  (): T
  // note the "?" after selector
  <U>(selector?: StateSelector<T, U>, equalityFn?: EqualityChecker<U>): U
}

As useContextStore(undefined, myEquals) is also valid.

Should I correct it in #725 or make a new PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That causes the DX issue in #812.

https://tsplay.dev/WJq5Vm

We don't recommend/allow passing undefined for selector as a practice. So, think it like TS types are correct, and JS impl is just naive. (We could use arguments, but it's not worth the size&complexity.)

btw, this is the similar discussion for combine in #725 (comment). TS types are correct for usage, so no need to be too precise about typing JS impl (if it's not the recommended usage. Could be done with defineProperty, but...)

Copy link
Contributor Author

@devanshj devanshj Apr 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's trivial to fix, we can just add another overload — https://tsplay.dev/wggQbw — we already have 2 overloads so adding another doesn't make a difference.

I think not supporting undefined selector is an unnecessary compromise, what if users want to actually use custom equality function without selector then they'd have to do useStore(x => x, eq) which looks less expressive and less intuitive than useStore(undefined, eq).

So I insist that we add another overload, I'm doing it in #725, if you don't like it I'll revert.

And in case of combine we're being intentionally unprecise with types to make the inference work, just that I think it's important to inform the users about the unsoundness we have produced, even if it is negligible. (Also shallow merge is good, defineProperty would make it more confusing and won't even solve the problem.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like useStore(undefined, eq), and it's almost always a mistake in the usage. But, having another overload is acceptable for an edge case. Will look at #725 and comment there.

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

Successfully merging this pull request may close these issues.

useCallback selector loses type inference in version 3.7.0
3 participants