Dev Study
← 境界の設計
レッスン 4 / 10·約 9 分·第14期 境界の設計

型は、線を越えない — 届くのはバイト列だけ

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口
  6. アプリ処理今ここ
  7. DB保存

前回まで、変更の境界をどこに引くかを見ました。今回はその線の上を実際に何が渡っているのかを見ます。

まず結論

同じプロセスの中では、引数の型は言語処理系が守ります。しかし線を越えた瞬間に届くのはバイト列だけで、型は「送る側の主張」と「受ける側の解釈」の2つに分かれます。

この2つが食い違っても例外は出ません。だから約束を、コードの外側に別に置く必要が生まれます。

? 読む前に予測

JavaScript で `{ when: new Date(...), tag: new Set(["a"]), inf: Infinity }` を JSON.stringify に渡すと、何が起きるでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

プロセスの中では、誰かが型を見ている

同じプロセスの中で関数を呼ぶとき、引数の型が合っているかを見るのは言語処理系です。呼ぶ側と呼ばれる側を同時に手元に持つので、食い違いはその場で指摘できます。

線を越えると、この前提が消えます。運ばれるのはバイト列だけで、そこに「これは Date です」と書く欄はありません。運ばれた先にあるのは、受け取った側が自分の規則でそのバイト列を読み解いた結果です。

つまり型は消えるのではなく、2つに分かれます。

型が2つに分かれる
送る側の型Date として持っている
変換
バイト列型の欄は無い
運ばれるのはこれだけ
読み解き
受ける側の型string として持つ
  • ・両側とも自分の型は持っている。一致する保証だけが無い
  • ・食い違いを検査する主体が、線の上にはいない
具体実物

1行で、4つが無言で壊れる

JavaScript の値を JSON に変換し、そのまま読み直しました。Docker 上の node:24-alpine(Node v24.21.0、2026-09-18 取得)です。

送る側 — JSON.stringify に渡した結果コマンド出力
> const p2 = { when: new Date("2026-09-18T00:00:00Z"), tag: new Set(["a"]),
miss: undefined, inf: Infinity, nan: NaN };
> JSON.stringify(p2)
{"when":"2026-09-18T00:00:00.000Z","tag":{},"inf":null,"nan":null}
受ける側 — 同じバイト列を読み直した結果コマンド出力
when typeof: string | instanceof Date: false
tag typeof: object | is a Set: false | keys: []
inf value : null | nan value: null
miss in obj: false

when は Date を送ったのに string で届きました。tag は Set を送ったのに中身の無いオブジェクト、inf と nan は null、miss に至ってはキーごと出力に現れていません。

そして重要なのは、この4つのどれも例外を出していないことです。送る側は成功として返し、受ける側も正常に読めています。どちらのログにも異常は残りません。

同じ操作で止まるものが1つあります。

BigInt を混ぜた場合コマンド出力
> const payload = { when: new Date("2026-09-18T00:00:00Z"), amount: 10n ** 2n };
> JSON.stringify(payload)
STRINGIFY THREW: TypeError: Do not know how to serialize a BigInt

5つのうち気づけたのは1つです。

✓ ここまでの確認

実測で BigInt だけが TypeError で止まりました。この事実の読み方として正しいのはどれですか。

抽象原理

運べない型は、運べないと規格が書いている

Infinity と NaN が null になったのは不具合ではありません。RFC 8259 §6 の一文です。

Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted.

JSON の文法にそれらを書き表す方法が無いので、送る側は置き換えるか止まるかしかありません。null への置き換えは ECMA-262 の JSON.stringify の手順です。不具合ではなく規格どおりです。

同じ §6 は数値の精度についても「実装が制限を設けてよい」とだけ書き、上限を定めていません。この点は第7期の『JSONとAPI — ブラウザ以外の相手に返す』が 2^53 の実測つきで扱っているので、そちらへ送ります。

ここから一般化できます。線を越えられるのは、その形式の文法で書ける値だけです。言語が持つ型の集合と、形式が運べる値の集合は別物で、両者は一致せず、どちらの向きにもはみ出します(1e400 は JSON で書けても Number では表せません)。言語側からはみ出した分は、置き換えられるか、消えるか、止まるかのいずれかになります。

具体実物

では、約束をどこに置くのか

型の一致を線の上で担保できないなら、取り決めを別の場所に置くことになります。この置き場所として広く使われているのが OpenAPI ですが、一つ確認すべきことがあります。

OpenAPI Specification v3.2.1(2026-09-18 取得)の冒頭は、自らをこう定義します。

The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for HTTP APIs

interface description — 記述です。続けて、成果物が何に使われるかを列挙します。

An OpenAPI Description (OAD) can then be used by documentation generation tools to display the API, code generation tools to generate servers and clients in various programming languages, testing tools, and many other use cases.

挙がっているのは文書生成・コード生成・テストです。and many other use cases で開いてはいますが、実行中のリクエストを検査する用途は挙げられていません。さらに §4.24.5.1 は一部のキーワードについてこう書きます。

The format keyword ... and the contentMediaType, contentEncoding, and contentSchema keywords define constraints on the data, but are treated as annotations instead of being validated directly. Extended validation is one way that these constraints MAY be enforced.

ここから本教材はこう読みます。 仕様は、書かれた取り決めが実行時に守られることを義務づけていません。「守られない」と書いてあるのではなく、義務づけが書かれていないという不在です。両者は違います。

✓ ここまでの確認

Infinity と NaN が null になったことは、何を意味しますか。

⇄ 2つを見比べる
具体例 A

運べない型を渡した場合

Date や Set や undefined は、JSON の文法にそれを表す書き方がありません。文字列に直すか、空のオブジェクトにするか、キーごと落とすかは、送る側の変換規則が決めます。受け取る側は届いたものをそのまま読むだけです。

送った型と、受け取った型表
送った型 線の上 受け取った型
Date "2026-09-18..." string
Set(["a"]) {} object(キー0個)
undefined (出力に無い) キーごと不在
具体例 B

運べる型だが、受ける側で表しきれない場合

大きな整数は JSON の文法で正しく書けます。線の上のバイト列には正しい桁が並びます。それでも受け取る側の数値型によっては値が変わります。第7期のレッスンが実測つきで扱った範囲です。

どちらの側で値が決まるか表
原因の層 決めるもの 例外
運べない型 送る側の変換規則 出ない
表しきれない 受ける側の数値型 出ない

どちらも例外が出ず、値が変わります。切り分けるには、何を見ればよいでしょうか。

線の上のバイト列を見たとき、送ったはずの値が書かれているかどうかで分かれます。

この章のまとめ

  • 線を越えて運ばれるのはバイト列だけ。型は「送る側の主張」と「受ける側の解釈」に分かれる。
  • 実測では Date → 文字列、Set → 空オブジェクト、undefined → キーごと消滅、Infinity・NaN → null の4つが無言で壊れ、BigInt だけが例外で止まった。
  • Infinity と NaN は RFC 8259 §6 で not permitted。null にするのは ECMA-262 の JSON.stringify。
  • 線を越えられるのは形式の文法で書ける値だけ。言語の型の集合のほうが広く、はみ出した分は置き換わるか消えるか止まる。
  • 一致を担保する主体が線の上にいないので、取り決めをどちらの内側でもない場所に置く必要が生じる。OpenAPI v3.2.1 は自らを interface description と定義し、用途に実行時の検査を挙げていない。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 4正解: 0

「線を越えると型が失われる」という言い方を、より正確にするとどうなりますか。

用語集(4語)
送る側の主張 / 受ける側の解釈(本教材での言い方)
線を越える通信で、型が2つに分かれた状態を指す言い方。両側とも自分の言語の型を持つが、その2つが一致する保証は線の上に無い。
JSON.stringify
JavaScript の値を JSON テキストへ変換する関数。JSON の文法で書けない型は、別のものに置き換えるか例外を投げる。どちらになるかは型ごとに決まっている。
OpenAPI Description (OAD)
OpenAPI Specification v3.2.1 が自らの成果物を呼ぶ用語。Description(記述)であって、強制の仕組みを指す語ではない。
interface description
OpenAPI Specification v3.2.1 の冒頭が自らを定義する語。HTTP API のインタフェースを、プログラミング言語に依らず記述する標準、という位置づけ。