Giter Club home page Giter Club logo

Comments (8)

RyanCavanaugh avatar RyanCavanaugh commented on July 17, 2024

This is tricky. By default this is done with a variance-based short circuiting; not doing this would be catastrophic to performance. Since it's only needed to probe the type deeper if it's the case that T is only referenced in places where it's unioned with null, a better workaround would be to do something like this

type RefObject<T> = _RefObject<Exclude<T, null>>;
interface _RefObject<T> {
    current: T | null
}

from typescript.

devongovett avatar devongovett commented on July 17, 2024

Ah interesting. I figured it was for performance. I guess your workaround would need to be added to the old versions of the React types (e.g. 18, 17, 16, etc), since React 19 removed the | null. Not sure if they'd be open to that and it would require everyone update to a new patch.

The other alternative we were considering for our library was to have our own copy of RefObject locally and use that everywhere instead of the one imported from react. That should also work but it means that each library that wants to maintain compatibility with multiple React versions will need to do that.

from typescript.

eps1lon avatar eps1lon commented on July 17, 2024

Not sure if they'd be open to that and it would require everyone update to a new patch.

If it's not breaking, you can file a PR and we see if it integrates cleanly.

type RefObject<T> = _RefObject<Exclude<T, null>>;
interface _RefObject<T> {
  current: T | null
}

This is just really gnarly and given how unpredictable type emission is in TypeScript is with numerous issues closed as intended or left unaddressed, I'd want to consider first if we want to merge this. There are already enough types to deal with as learners and adding some weird _ prefixed types just makes it worse if they leak into user code.

from typescript.

devongovett avatar devongovett commented on July 17, 2024

Yeah... maybe ok if it was an internal type but still not ideal that projects would need to upgrade the 16/17/18 types in order to fix compatibility with libraries built for the 19 types either.

Would your recommendation for libraries that need to support multiple React versions be to make their own copy of RefObject like we're thinking of doing in React Aria? adobe/react-spectrum#6632

from typescript.

eps1lon avatar eps1lon commented on July 17, 2024

not ideal that projects would need to upgrade the 16/17/18 types in order to fix compatibility with libraries built for the 19 types either.

I don't think this is an issue. I think if we can't even recommend updating libraries by a patch version, we've lost the ability to move the ecosystem forward. We can recommend upgrading React types by a patch once, or teach each library how to add a compat layer and then rely on consumers to upgrade each of these libraries? Seems way more simple to fix this in React types.

But if it requires this workaround that may leak into emitted types, it doesn't seem like a good workaround. Regardless of whether we do this in React types or 3rd party libraries. It would be more helpful if TypeScript would either fix this or improve type emission so that libraries are in control of what types are public and private.

Why can't you upgrade all your callsites of RefObject to use RefObject<T | null> right now ignoring anything that comes in React 19? You said it causes errors but I'm not seeing any in the playground nor do I see an explainer why it should error. The sample code you posted could be fixed if all RefObject usages would use RefObject<T | null>.

from typescript.

devongovett avatar devongovett commented on July 17, 2024

Why can't you upgrade all your callsites of RefObject to use RefObject<T | null> right now

Yeah that's what we've done. The problem occurs when passing a ref typed as RefObject<T | null> to anything that accepts React.RefAttributes<T> in previous versions of React, including builtin JSX elements like <div>, and any forwardRef components.

Here's a more practical example of what I mean: Playground

In React 19, ForwardedRef and RefAttributes include RefObject<T | null> but in previous versions of React, the | null is inside the definition of RefObject. So due to this TS limitation, if we add T | null to our definition, we are compatible with React 19 but not 18 or previous, and if we don't add it we are compatible with previous but not 19.

from typescript.

eps1lon avatar eps1lon commented on July 17, 2024

That is a problem that I would love being able to fix in React types. But this _RefObject types appears in emitted declarations and we just created another problem that'll take years to get rid of when TypeScript changes behavior in that area. And there's the cost of having to learn/teach why we have this intermediate type as well.

from typescript.

devongovett avatar devongovett commented on July 17, 2024

Yeah I agree. Ideally TS would fix this issue and prioritize correctness over performance. But in the meantime we'll need a recommendation because I would imagine we are not the only library that will be affected by this.

Another option I thought of would be to add a new NullableRefObject type in both old and new versions of the React types. In old react it could be an alias:

// In old react types
type NullableRefObject<T> = RefObject<T>;

and in React 19 it could add null to T:

// In React 19
type NullableRefObject<T> = RefObject<T | null>;

Then libraries could use NullableRefObject everywhere. Perhaps slightly less hacky but still requires updating many versions of the types, and teaching a new type. 🤔

from typescript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.