feat: add optional @types/react peer dependency - #276
Open
unrevised6419 wants to merge 1 commit into
Open
unrevised6419 wants to merge 1 commit into
unrevised6419 wants to merge 1 commit into
Conversation
unrevised6419
force-pushed
the
feat/optional-types-react-peer
branch
from
September 24, 2026 03:06
519da88 to
58e9634
Compare
The published declarations import `react` for its types, but only the `react` runtime peer is declared. Package managers that install the package outside the consumer's repository (pnpm's global virtual store) therefore leave no `@types/react` reachable from the shipped `.d.ts` files, and `React.*` resolves to the untyped `react/index.js` or to nothing at all. Declaring `@types/react` as an optional peer at `>= 18`, mirroring the `react` peer this package already declares, makes the types resolvable without affecting non-TypeScript consumers and without pinning a specific version.
unrevised6419
force-pushed
the
feat/optional-types-react-peer
branch
from
September 24, 2026 03:19
58e9634 to
24bdec6
Compare
unrevised6419
marked this pull request as ready for review
September 24, 2026 03:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
@types/reactas an optional peer dependency at">= 18".The symptom
On a TypeScript app using pnpm with
virtualStoreType: global,react-countdowndoes not degrade quietly — it fails to compile:Every
<Countdown />usage site errors. The app has@types/reactinstalled andreacton a compatible version; nothing about the setup is exotic apart from the store layout.Why it happens
react-countdownships.d.tsfiles that importreactfor its types, but declares only thereactruntime peer.dist/Countdown.d.tsin 2.3.6 starts:Under pnpm's global virtual store the package is not installed inside the consuming repository. It lives in the shared store, e.g.
TypeScript resolves a package's imports from where that package physically sits, and it ignores
NODE_PATH. Walking up from that store path, the onlyreactit can see is the one pnpm linked in to satisfy the declared runtime peer — thereactpackage itself, which ships no declarations. The consumer's@types/reactis never on that path, because nothing told pnpm it was needed there.tsc --noEmit --traceResolution:index.js, notindex.d.ts. That is the whole bug. Where the JS fallback is accepted (allowJs: true) it is silent and every React-derived type in the public API becomesany. Where it is not — react'sexportsmap offers notypescondition and there is noindex.d.tsbesideindex.js—reactresolves to nothing,React.Componentis unresolved, the class's base type collapses to{}, and TS2786 fires. That second case is what the error above is.masteris affected in the same way. The v3 declarations still reach intoreact;pnpm buildon3.0.0-beta.0emits:If
reactresolves to nothing there, the default export's type collapses and the same class of JSX error follows.The fix
That is enough for pnpm to place the consumer's
@types/reactnext toreact-countdownwherever it installs it, and thetraceResolutionline above becomes@types/react/index.d.ts.Why it is safe
">= 18", mirroring thereact: ">= 18"peer this repo already declares, so the types peer adds no version claim of its own. This repo develops against@types/react@^19, which the range includes.distis byte-identical.pnpm installafter the change leavespnpm-lock.yamlunmodified.pnpm lintandpnpm test(58 tests, 6 snapshots) pass unchanged.Precedent
@testing-library/react— already a devDependency here — declares exactly this shape, and has since v13:I mirrored the repo's existing
react: ">= 18"peer rather than pinning a version: the point is to make the types reachable, not to havereact-countdownnarrow which@types/reacta consumer runs.I verified this end to end on a real application. Adding these peers for the affected packages via pnpm
packageExtensionstook it from 61 untyped and 6 unresolved React resolutions to 1012/1012 landing on a.d.ts, withtscstill exiting 0.Related: #90 reported a very similar-looking JSX error (
Type 'Countdown' is missing the following properties from type 'ElementClass') back in 2022. That one had a different root cause and was fixed in v2.2.2, but the failure mode is the same — an unresolvableReactin the shipped declarations reads to the consumer as "this component is not a component".Happy to also add
@types/react-domalongside it if you would prefer to mirror@testing-library/reactexactly, though nothing in the current declarations importsreact-dom.