Comments (8)
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.
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.
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.
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.
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.
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.
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.
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)
- Investigate Wildcard Matching Performance HOT 2
- How to correctly obtain the type of return value after the decorator HOT 1
- The type reduction of the in operator is invalid in ts 5.5 HOT 7
- `getTypeListId` crashes with `TypeError: Cannot read properties of undefined (reading 'id')`
- Suggestion: A use case requires the "nameof" operator HOT 3
- Overload call signature resolution resolves to first overload HOT 11
- FR: Add TS config option to disable "<some regex feature> is only available when targeting 'es2018' or later.". HOT 5
- Deprecating no-default-lib and rethinking of other lib related CompilerOptions HOT 1
- Function overloading of generic functions allows for invalid code HOT 1
- TS7022 (circular reference) on simple assignments in a loop HOT 4
- Suggestion xml2typescript tooling HOT 4
- Signature help stopped expanding type alias in TypeScript 5.5 HOT 7
- Problem with .d.ts file generation for inner classes HOT 4
- Field overwriting with stage 3 decorators changes parent class metadata HOT 1
- [NewErrors] 5.6.0-dev.20240630 vs 5.5.2 HOT 10
- [ServerErrors][JavaScript] 5.6.0-dev.20240630 HOT 9
- [ServerErrors][TypeScript] 5.6.0-dev.20240630 HOT 22
- Extend type predicate inference to work with certain type variables HOT 2
- Param with generic function type extends `Window` can't compare to `window`
- using a toLowerCase function inside the if block type error HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typescript.