セマンティックバージョニングと package.json のバージョン指定 - ^ と ~ の違い、0.x の罠

セマンティックバージョニングと package.json のバージョン指定 - ^ と ~ の違い、0.x の罠

作成日:
読了:8
更新日:

package.json"react": "^18.2.0"^、なんとなく付けていませんか。この記号ひとつで「次の npm install でどのバージョンまで上がるか」が変わります。この記事では、セマンティックバージョニング(semver)と npm のバージョン指定を、semver.org・npm 公式・node-semver を一次ソースに整理します。

セマンティックバージョニングとは

バージョンは MAJOR.MINOR.PATCH の3つの数値。上げる基準は仕様で決まっています。

いつ上げるか
MAJOR後方互換性を壊す API 変更をしたとき
MINOR後方互換を保ったまま機能追加したとき
PATCH後方互換のあるバグ修正をしたとき

上位を上げたら下位はゼロに戻します(MINOR を上げたら PATCH は 0)。1.0.0 で「公開 API を定義する」とされ、以降この公開 API の変化に応じて番号を動かします。

0.y.z(初期開発版)の特別扱い

仕様には「メジャー0(0.y.z)は初期開発用で、いつ何が変わってもよい。公開 API は安定とみなすべきでない」と明記されています。この特例が、後述する ^ の挙動にも効いてきます。

プレリリースとビルドメタデータ

  • プレリリース版: 1.0.0-alpha1.0.0-rc.1 のようにハイフンで付ける。通常版より優先順位は低い1.0.0-alpha1.0.0 より前)
  • ビルドメタデータ: 1.0.0+20130313 のようにプラスで付ける。優先順位の比較では無視される

優先順位の例(仕様より): 1.0.0-alpha1.0.0-alpha.11.0.0-beta1.0.0-rc.11.0.0

キャレット ^ とチルダ ~

ここが本題です。npm の範囲指定で最頻出の2つを比べます(範囲の厳密な定義元は node-semver)。

指定許可される範囲上がる桁
^1.2.3>=1.2.3 かつ <2.0.0MINOR・PATCH
~1.2.3>=1.2.3 かつ <1.3.0PATCH のみ
  • ^(キャレット): 「いちばん左の0でない桁」を固定し、それ未満を更新。^1.2.3 なら MINOR と PATCH が上がる
  • ~(チルダ): MAJOR.MINOR.PATCH が揃っていれば PATCH のみ更新

つまり通常は ^ のほうが広く(MINOR まで)、~ のほうが狭い(PATCH のみ)

0.x の罠: ^ が ~ に縮退する

初学者が必ずハマるのがこれです。0.x では ^ の挙動が変わります(前述の「メジャー0は不安定」が反映されるため)。

指定許可される範囲実質
^0.2.3>=0.2.3 かつ <0.3.0PATCH のみ~0.2.3 と同じ!)
~0.2.3>=0.2.3 かつ <0.3.0PATCH のみ
^0.0.3>=0.0.3 かつ <0.0.4更新なし(実質固定)

^1.x は MINOR まで上げるのに、^0.2.3 は MINOR を上げません。「メジャー0の MINOR 更新は破壊的変更でありうる」という semver の考えに沿った挙動です。依存が 0.x のうちは ^ でも MINOR は上がらない——これを知らないと「なぜ更新されない/されすぎる」で混乱します。

その他の範囲指定

  • x / *: 1.2.x>=1.2.0 かつ <1.3.0* は任意
  • ハイフン範囲: 1.2.3 - 2.3.4 は両端を含む(>=1.2.3 かつ <=2.3.4
  • OR: ^2 || ^3 || ^4 でいずれかを満たせば OK
  • 完全一致: 4.18.2 のように記号なしで固定

NOTE

範囲指定はデフォルトでプレリリース版を除外します(^1.2.32.0.0-rc.1 を含まない)。プレリリースを拾うには範囲側にもプレリリースタグを書く必要があります。

package.json と package-lock.json

範囲指定だけでは「いつ誰がインストールしても同じバージョン」は保証されません。そこを担うのが package-lock.json です。

  • package.json: 範囲^1.2.3 等)を書く=「これくらいの幅で許す」という意思表示
  • package-lock.json: 実際に入った正確なバージョンを記録=「次回も同一ツリーを再現」する。ソース管理にコミットするのが公式の想定

この2層構造で「柔軟な範囲指定」と「再現性」を両立します。

npm install / ci / update の違い

コマンド挙動
npm install範囲を満たす形で解決し、node_modules と lock を生成・更新(lock を書き換えうる)
npm cilock 必須・lock に厳密追従。lock と package.json が不整合ならエラーで停止し、書き換えない(CI 向け・決定的)
npm updatesemver の範囲を尊重して最新へ更新(^1.1.1 なら同一 MAJOR 内の最新へ)。lock を更新

CI やデプロイでは npm ci を使うと、毎回まったく同じ依存が入って事故が減ります。

実務の指針

  • ライブラリ作者は semver を守る1.0.0 で公開 API を確定し、互換を壊すなら MAJOR を上げる。0.x の間は「不安定」と明示する
  • アプリは package-lock.json をコミットし、CI で npm ci。範囲の広さに関わらず実バージョンを固定できる
  • ^ のメリットとリスク: バグ修正・機能追加を自動で取り込める反面、提供側が semver を守らないと壊れる。lock があれば実害は出にくいが、lock を持たない/更新する場面では「範囲内の新しい版」が入る点に注意
  • 依存が 0.x なら ^ でも MINOR は上がらないことを前提に置く

まとめ

  • semver は MAJOR(破壊的)・MINOR(互換ある機能追加)・PATCH(互換あるバグ修正)
  • ^ は MINOR まで、~ は PATCH のみ。ただし 0.x では ^0.2.3~ と同じ・^0.0.3 は実質固定
  • プレリリースは範囲からデフォルト除外、ビルドメタデータは順序比較で無視
  • package.json=範囲、package-lock.json=固定の2層。再現性は lock + npm ci で担保
  • npm install は lock を更新しうる、npm ci は lock に厳密追従、npm update は範囲内で最新へ

^~、そして 0.x の特例を理解しておくだけで、「勝手に壊れた」「更新されない」の多くは説明がつきます。範囲は意思表示、固定は lock——この役割分担が要です。

参考リンク

Node.jsが年1回のメジャーリリースへ - Node 27からの新スケジュールとLTSの考え方

Node.jsが年1回のメジャーリリースへ - Node 27からの新スケジュールとLTSの考え方

7

Node.js が 2026年3月の公式発表でリリーススケジュールを変更します。Node 27(2027年)から、メジャーは年1回(毎年4月)に集約され、奇数/偶数の区別を廃止して全リリースが LTS 対象に。早期テスト用の Alpha チャンネル新設、総サポート期間の36か月化など、新旧スケジュールの違いと、Node 26 / Node 27 の位置づけ、運用への影響を Node.js 公式ブログを一次ソースに整理します。

TypeScript 6.0 の新機能と破壊的変更 - JavaScript製で書かれる最後のコンパイラ

TypeScript 6.0 の新機能と破壊的変更 - JavaScript製で書かれる最後のコンパイラ

10

TypeScript 6.0(2026年3月23日リリース)を公式アナウンスとリリースノートをもとに整理します。6.0 が「JavaScript製の最後のコンパイラ」であり 7.0(Go製・Project Corsa)へのブリッジである位置づけ、this を使わない関数の推論改善・#/ サブパスインポート・es2025 lib・Temporal 型などの新機能、そして strict や module/target の既定値変更、amd/umd や outFile の削除、module 名前空間構文や import assert の廃止といった破壊的変更、ignoreDeprecations や stableTypeOrdering による移行までを一次ソースでまとめます。

Node.js 26 の注目点 - Temporal 既定有効と「ビルドなしTS実行」の現在地

Node.js 26 の注目点 - Temporal 既定有効と「ビルドなしTS実行」の現在地

8

2026年5月リリースの Node.js 26 を整理します。長く待たれた Temporal API のデフォルト有効化、type stripping によるビルドなし TypeScript 実行の安定化(と --experimental-transform-types の削除が意味すること)、V8 14.6・Undici 8 への更新、レガシー stream モジュールの削除など、移行前に押さえておきたい差分をまとめます。