Skip to content

fix: declare @types/react as an optional peerDependency - #230

Open
jiwooIncludeJeong wants to merge 2 commits into
toss:mainfrom
jiwooIncludeJeong:codex/optional-react-types-peer-dependency
Open

fix: declare @types/react as an optional peerDependency#230
jiwooIncludeJeong wants to merge 2 commits into
toss:mainfrom
jiwooIncludeJeong:codex/optional-react-types-peer-dependency

Conversation

@jiwooIncludeJeong

@jiwooIncludeJeong jiwooIncludeJeong commented Jun 16, 2026

Copy link
Copy Markdown

Summary

overlay-kit exposes React types in its public API (the bundled dist/index.d.ts imports FC from react and re-exports react/jsx-runtime), but it only declares react as a peer dependency — not @types/react. As a result, in strict / isolated installs (pnpm, Yarn PnP) consumers' TypeScript cannot resolve the React typings that overlay-kit's declarations depend on, and the entire public API silently collapses to any.

This PR adds @types/react as an optional peer dependency so the types resolve correctly for TypeScript consumers, while not affecting JavaScript-only users.

Root cause

overlay-kit/dist/index.d.ts does:

import { FC } from 'react';
// ...
type OverlayControllerComponent = FC<OverlayControllerProps>;

With pnpm's isolated node_modules, the overlay-kit package directory only has the declared react runtime peer linked — @types/react is not reachable from overlay-kit's module-resolution ancestry. TypeScript therefore resolves the bare react import to React's typeless runtime index.js, so:

  • FC resolves to any
  • OverlayControllerComponent = FC<...> becomes any
  • overlay.open(...)'s controller argument becomes any

Consumers then lose all type safety, e.g.:

  overlay.open(({ isOpen, close }) => <Modal open={isOpen} onClose={close} />);
  //             ^^^^^^  ^^^^^  → both are implicitly `any` (TS7031 under noImplicitAny)

and Parameters[0] resolves to unknown.

This is masked under npm / Yarn Classic because their flat hoisting accidentally places @types/react at the top level where overlay-kit's declarations can reach it. It is a genuine missing declaration that strict package managers correctly surface.

Fix

  // package.json
  {
    "peerDependencies": {
      "react": "^16.8 || ^17 || ^18 || ^19",
      "@types/react": "*"
    },
    "peerDependenciesMeta": {
      "@types/react": {
        "optional": true
      }
    }
  }
  • peer, not dependency — the React types must match the consumer's @types/react version; pinning a dependency would cause duplicate/mismatched type instances.
  • optional — JavaScript-only consumers (no @types/react installed) must not get an unmet-peer warning.
    • range — the declarations adapt to whatever @types/react version the consumer has; narrowing it would cause false conflicts in mixed React 18/19 workspaces.

This matches the standard pattern used across the React + TypeScript ecosystem (e.g. @radix-ui/*, framer-motion) for libraries that expose React types publicly.

Reproduction

pnpm add overlay-kit @types/react

  import { overlay } from 'overlay-kit';

  // Expected: isOpen: boolean, close: () => void
  // Actual (before this PR): both implicitly `any`
  overlay.open(({ isOpen, close }) => { /* ... */ return null; });

Impact

  • No source / runtime changes — only package.json peer metadata.
  • TypeScript consumers on strict installs regain full typing of the overlay.open / overlay.openAsync controller props.
  • JavaScript consumers are unaffected (peer is optional).

요약

overlay-kit은 public API에 React 타입을 노출합니다 (배포되는 dist/index.d.tsreactFC를 import하고 react/jsx-runtime을 재노출). 하지만 peer dependency로는 react만 선언하고 @types/react는 선언하지 않습니다. 그 결과 엄격/격리 설치 환경(pnpm, Yarn PnP)에서는 소비자의 TypeScript가 overlay-kit 선언이 의존하는 React 타입을 해석하지 못하고, any로 추론되게 됩니다.

이 PR은 @types/reactoptional peer dependency로 추가해, TypeScript 소비자에서 타입이 정상 해석되도록 하고 JavaScript 전용 사용자에게는 영향을 주지 않습니다.

근본 원인

overlay-kit/dist/index.d.ts:

import { FC } from 'react';
type OverlayControllerComponent = FC<OverlayControllerProps>;

pnpm의 격리된 node_modules에서는 overlay-kit 패키지 디렉토리에 선언된 react 런타임 peer만 링크되고, @types/react overlay-kit의 모듈 해석 조상 경로에서 닿지 않습니다. 따라서 TypeScript는 react import를 타입이 없는 런타임 index.js로 해석하고:

  • FC → any
  • OverlayControllerComponent = FC<...> → any
  • overlay.open(...)의 controller 인자 → any

소비자는 타입 안전성을 모두 잃습니다:

  overlay.open(({ isOpen, close }) => <Modal open={isOpen} onClose={close} />);
  //             ^^^^^^  ^^^^^  → 둘 다 implicit any (noImplicitAny에서 TS7031)

Parameters[0]도 unknown으로 해석됩니다.

npm / Yarn Classic에서는 flat hoisting이 @types/react를 top-level에 올려서 우연히 해석되기 때문에 가려질 뿐, 실제로는 누락된 선언입니다. 패키지 매니저가 이를 정확히 드러냅니다.

수정

{
  "peerDependencies": {
    "react": "^16.8 || ^17 || ^18 || ^19",
    "@types/react": "*"
  },
  "peerDependenciesMeta": {
    "@types/react": { "optional": true }
  }
}
  • dependency가 아닌 peer — React 타입은 소비자의 @types/react 버전에 맞춰야 함. dependency로 추가하면 타입 인스턴스 중복/불일치 발생.
  • optional — @types/react가 없는 JS 전용 소비자에게 unmet-peer 경고가 뜨지 않도록.
    • 범위 — 선언이 소비자의 @types/react 버전에 적응. 범위를 좁히면 React 18/19 혼용 워크스페이스에서 잘못된 충돌 유발.

이는 React 타입을 public하게 노출하는 라이브러리(@radix-ui/*, framer-motion 등)가 쓰는 표준 패턴과 동일합니다.

@changeset-bot

changeset-bot Bot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 22935d9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
overlay-kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

@jiwooIncludeJeong is attempting to deploy a commit to the Toss Team on Vercel.

A member of the Team first needs to authorize it.

@jiwooIncludeJeong
jiwooIncludeJeong marked this pull request as ready for review June 17, 2026 00:52
Comment thread packages/package.json Outdated
"vitest": "^2.1.8"
},
"peerDependencies": {
"@types/react": "*",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there a specific reason why the peer dependency version for @types/react is set to *?

How about setting it to "^16.8 || ^17 || ^18 || ^19" to align with the actual runtime versions required by React?

Packages like base-ui also use explicit version specifications instead of *.

https://github.com/mui/base-ui/blob/master/packages/react/package.json#L148

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

There is no specific reason! I will edit it and re-request review soon! Thanks!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

feat: specify @types/react version i commited the changes!

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.

2 participants