Skip to content

feat: add optional @types/react peer dependency - #414

Open
unrevised6419 wants to merge 1 commit into
dip:mainfrom
unrevised6419:feat/optional-types-react-peer
Open

unrevised6419 wants to merge 1 commit into
dip:mainfrom
unrevised6419:feat/optional-types-react-peer

Conversation

@unrevised6419

@unrevised6419 unrevised6419 commented Sep 24, 2026 •

Copy link
Copy Markdown

What

Adds @types/react as an optional peer dependency at ^18 || ^19 on the cmdk package.

"peerDependencies": { "@types/react": "^18 || ^19" },
"peerDependenciesMeta": { "@types/react": { "optional": true } }

No runtime behaviour changes — this is metadata only.

Why

dist/index.d.ts starts with import * as React from 'react', but the package declares only the react / react-dom runtime peers. TypeScript resolves a package's imports from where that package physically sits on disk, and it ignores NODE_PATH.

That is fine in a classic node_modules layout, where the consumer's @types/react is reachable by walking up. It breaks under pnpm's virtualStoreType: global, where cmdk lives outside the consumer's repository entirely, e.g. ~/Library/pnpm/store/v11/links/…/node_modules/cmdk. From there, react has no types to resolve to, so TS falls back to the untyped react/index.js (silently accepted with allowJs: true) or fails to resolve at all.

tsc --noEmit --traceResolution in a consumer using moduleResolution: "bundler" with @types/react installed:

======== Resolving module 'react' from '<store path>/cmdk/dist/index.d.ts'. ========
======== Module name 'react' was successfully resolved to '<store path>/react/index.js' with Package ID 'react/index.js@19.3.0'. ========

index.js, not index.d.ts. Every React.* type in cmdk's declarations — React.ReactNode, the ComponentPropsWithoutRef bases for Command, Command.Input, Command.Item, the ref types — degrades to any, and the compiler reports nothing. Consumers lose autocomplete and prop checking on cmdk without any signal that it happened. That silence is what makes it worth fixing.

With the optional peer declared, pnpm links @types/react next to the package and the same trace resolves to @types/react/index.d.ts.

Why it is safe

  • Optional — consumers not using TypeScript get no install warning and nothing added to their lockfile.
  • ^18 || ^19 — it mirrors the existing react peer, so consumers on either major are covered and nothing new is installed for anyone who does not already have the types.

This repo develops against @types/react 18 while the runtime peer allows ^18 || ^19; the types peer mirrors that union rather than narrowing it to 18.

Precedent

@testing-library/react declares exactly this pair, for exactly this reason.

Verification

Measured on a real application by applying the same peers through pnpm packageExtensions: the app went from 61 untyped + 6 unresolved React resolutions to 1012/1012 landing on .d.ts, with tsc still exiting 0.

@unrevised6419
unrevised6419 force-pushed the feat/optional-types-react-peer branch from 3b4c798 to 466cc14 Compare September 24, 2026 03:08
The published dist/index.d.ts imports React types, but the package only
declares the react/react-dom runtime peers. Under pnpm layouts where the
package lives outside the consumer's repo, TypeScript resolves that import
from the package's physical location and silently falls back to the untyped
react/index.js, degrading every cmdk type to any with no compiler error.

Declaring @types/react as an optional peer at "^18 || ^19", mirroring the
existing react peer, makes the types resolve without affecting
non-TypeScript consumers or any lockfile.
@unrevised6419
unrevised6419 force-pushed the feat/optional-types-react-peer branch from 466cc14 to e5ab1da Compare September 24, 2026 03:19
@unrevised6419
unrevised6419 marked this pull request as ready for review September 24, 2026 03:28

This branch has not been deployed

No deployments
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.

1 participant