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

Rewrite the documentation about why the typescript types can not be inferred #2900

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

GuichiZhao
Copy link

As discussed in #2895

Copy link

vercel bot commented Dec 14, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
zustand-demo ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 14, 2024 0:36am

Copy link

codesandbox-ci bot commented Dec 14, 2024

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.

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.

I'll leave @devanshj for detailed reviews.

foo: 0,
bar: () => get(),
Copy link
Member

Choose a reason for hiding this comment

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

I think this is required to reproduce the problem. Otherwise, it would have been easier.

Copy link
Author

Choose a reason for hiding this comment

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

Bring back bar: () => get() and add some text about how the ts behavior related to real-world scenario

@devanshj
Copy link
Contributor

devanshj commented Jan 7, 2025

The problem with this PR is that it makes it look as if TypeScript is doing it's best and is correct whereas it can do better. I've opened an issue in TypeScript to know which feature will enable the inference we want.

Once we know that we can just link to that issue. I don't think we need this explanation of how TypeScript's current inference algorithm works as it's not that complicated now. Or maybe we can keep it. I'll let @dai-shi decide that.

@GuichiZhao
Copy link
Author

GuichiZhao commented Jan 10, 2025

The problem with this PR is that it makes it look as if TypeScript is doing it's best and is correct whereas it can do better. I've opened an issue in TypeScript to know which feature will enable the inference we want.

Once we know that we can just link to that issue. I don't think we need this explanation of how TypeScript's current inference algorithm works as it's not that complicated now. Or maybe we can keep it. I'll let @dai-shi decide that.

After reading the microsoft/TypeScript#49618 and microsoft/TypeScript#60922 as your issue referred, I admit that there are some point I am missing, it does relate to the "chicken and egg" problem

Let me prove this:
There can be an even simpler case

function test<T>(fn: (prev: T) => T) { }
test((prev) => ({ a: 1 })); // T is inferred as "unknown"

My initial point:
The reason why T is unknown is that there is no hint for prev

And the point can be easily broken if we try NoInfer

function test<T>(fn: (prev: NoInfer<T>) => T) { }
test((prev) => ({ a: 1 })); // T is inferred as "unknown"

We already tell the engine that never cares about prev when you did the inference, but it still does not work, so the root case has nothing to do with the "no type information"

As the typescript team pointed out

function test<T>(fn: (prev: NoInfer<T>) => T) { }
test((prev) => {
  return prev ? 1:0
});

if the output type relates to input type, typescript will refuse to do anything
it seems to me that the MS team believes such usage is an anti-pattern anyway.

That said, I'm not really sure what the endgoal is. Any "improvement" we make here is just going to sow a bunch of "TypeScript is inconsistent, therefore has bug" reports because people will wonder why
microsoft/TypeScript#49618 (comment)

maybe we should consider avoiding such usage altogether

declare const create: <T>() => T

const x = create(() => ({
  foo: 0,
  bar: () => {
    x.setState({foo:x.getState().foo+1})
  },
}))

// OR
const y=create(()=>({
  foo: 0,
})) 

const bar=() => {
  y.setState({foo:y.getState().foo+1})
}

Or simply use https://zustand.docs.pmnd.rs/middlewares/combine

@devanshj

@dai-shi
Copy link
Member

dai-shi commented Jan 10, 2025

As I wrote somewhere, Zustand's API design is JavaScript first, not TypeScript first. We are not changing the API anytime soon. The current typing is hacky, but it's been doing well. Moving forward, I think what we should do is to recommend combine or zustand-slices or some other slicing utils.

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.

3 participants