react-scrollが型エラーになってビルドが通らないので調査した

## 最初に結論

対応としては以下のどれかになる。プロダクト・プロジェクトの状況に応じて選択しましょう。

- Reactを 19 にアップグレードする
- 独自の型定義ファイル（global.d.tsとかindex.d.tsとか）を作って回避
- package-lock.json、yarn.lockを元に戻す
- バージョン管理をpnpmに変える
- yarn patchを使って@types/react-scrollの依存関係にある@types/reactのバージョンを'*' → '^18' とかに変更する


## 何が起きたか

起きた型エラーの内容は以下です。

```shell
'Link' cannot be used as a JSX component.
  Its type 'typeof Link' is not a valid JSX element type.
    Types of construct signatures are incompatible.
      Type 'new (props: LinkProps) => Link' is not assignable to type 'new (props: any, deprecatedLegacyContext?: any) => Component<any, any, any>'.
        Property 'refs' is missing in type 'Component<LinkProps, {}, any>' but required in type 'Component<any, any, any>'.ts(2786)
index.d.ts(1033, 9): 'refs' is declared here.
```

実装は以下のようにコンポーネント化していました。

```typescript
import { Link } from 'react-scroll';

export const ScrollLink: React.FC<Props> = ({
  className,
  children,
  duration = 500,
  offset = -100,
  onClick,
  to,
  activeClass = 'active',
}) => {
  return (
    <Link
      activeClass={activeClass}
      duration={duration}
      smooth={true}
      spy={true}
      {...{ className, offset, onClick, to }}
    >
      {children}
    </Link>
  );
};

type Props = {
  activeClass?: string;
  children: React.ReactNode;
  className?: string;
  duration?: number;
  offset?: number;
  onClick?: () => void;
  to: string;
};
```

## 原因

調べた限りでは React19 にてReact.Componentから、`refs` が削除されたからというのが根本原因でした。

`@types/react:18.3.16` では以下のように書かれてますが、これが `@types/react:19.0.1` では消えてます。


```typescript:index.d.ts
/**
         * @deprecated
         *
         * @see {@link https://legacy.reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs Legacy React Docs}
         */
        refs: {
            [key: string]: ReactInstance;
        };
```

該当のGithubも載せておきます。

@types/react v18:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d7ca55e97f79a85f8357f51ef2420b932917a6de/types/react/v18/index.d.ts#L1028C9-L1035C11

---

私は yarn を使ってパッケージ管理をしてます。　yarn.lockをみると `@types/react-scroll` の依存関係で `@types/react` が `*` で指定されており、その結果 `@types/react` の最新バージョンを依存関係として親切にもインストールしてくれるようです。


```yaml:yarn.lock(抜粋）
"@types/react-scroll@^1.8.10":
  version "1.8.10"
  resolved "https://registry.yarnpkg.com/@types/react-scroll/-/react-scroll-1.8.10.tgz#585a5c4bd0654434f3e55a08e94ed2e048bae7c7"
  integrity sha512-RD4Z7grbdNGOKwKnUBKar6zNxqaW3n8m9QSrfvljW+gmkj1GArb8AFBomVr6xMOgHPD3v1uV3BrIf01py57daQ==
  dependencies:
    "@types/react" "*"
```

package.jsonで以下のように依存関係を指定したんですが、解決せず。。

```json:package.json（抜粋）
{
  "resolutions": {
    "@types/react": "^18.3.16"
  }
}
```
または
```json:package.json（抜粋）
"resolutions": {
    "@types/react": "^18.3.12",
    "@types/react-scroll/@types/react": "^18.3.12",
    "@types/hoist-non-react-statics/@types/react": "^18.3.12",
    "@repo/next-js/@types/react-dom/@types/react": "^18.3.12"
  }
```

どうもパッケージの内部の依存関係についてはnpmもyarnも開発者側から指定するのは難しいみたいです。　
ただ、 `yarn patch` を使えばもしかしたらうまくいくかもしれません（未確認）
以下のように node_modules 配下を直接編集。

```json:node_modules/@types/react-scroll/package.json
"scripts": {},
    "dependencies": {
        "@types/react": "^18"
    },

```

その後 `yarn patch` を実行するます。

```
yarn patch @types/react-scroll
```


なお、pnpmだとこの辺の依存関係をもっと楽に解消してくれるようです。

https://pnpm.io/ja/how-peers-are-resolved

https://pnpm.io/symlinked-node-modules-structure

## 回避策（一時的）

冒頭に述べたようにReactを19に上げてしまえば基本問題ないのですが、プロダクトの仕様上簡単にはできんねん。。。という場合もあるでしょう。そのときの解決策は個別に型定義ファイルを作ってOverrideしてあげる感じになると思います。

ディレクトリ直下（monorepoの場合は各Appのディレクトリ配下）に `global.d.ts` みたいなファイルを作成し、以下を記述。

```typescript:global.d.ts
declare module 'react-scroll' {
  interface ReactScrollLinkProps {
    absolute?: boolean | undefined;
    activeClass?: string | undefined;
    activeStyle?: React.CSSProperties | undefined;
    containerId?: string | undefined;
    delay?: number | undefined;
    duration?: number | string | ((distance: number) => number) | undefined;
    hashSpy?: boolean | undefined;
    horizontal?: boolean | undefined;
    ignoreCancelEvents?: boolean | undefined;
    isDynamic?: boolean | undefined;
    offset?: number | undefined;
    onClick?(): void;
    onSetActive?(to: string, element: HTMLElement): void;
    onSetInactive?(to: string, element: HTMLElement): void;
    saveHashHistory?: boolean | undefined;
    smooth?: boolean | string | undefined;
    spy?: boolean | undefined;
    spyThrottle?: number | undefined;
    to: string;
  }

  type LinkProps = ReactScrollLinkProps & React.HTMLProps<HTMLButtonElement>;

  class Link extends React.Component<LinkProps> {}

  export { Link };
}
```

package.jsonでは以下のように`@types/react`と `@types/react-scroll` をバージョンを指定してインストール。

```json:package.json（抜粋）
"devDependencies": {
  "@types/react": "^18.3.12",
  "@types/react-scroll": "^1.8.10",
}
```

これで実装側では型定義を `@types/react": "^18.3.12"` のパッケージに対して参照してくれます。ただし、永遠にこの対策をしているとreact-scrollのバージョンアップに追随できないので注意しましょう。