From 38e8ed514f9d2bffb6553b3f79955742b02c3e44 Mon Sep 17 00:00:00 2001
From: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
Date: Mon, 24 Aug 2026 14:51:22 +0530
Subject: [PATCH] Add lib types for JSON.rawJSON, JSON.isRawJSON, and reviver
context
Port proposal-json-parse-with-source (ES2025) lib typings onto the Go
tree layout:
- tsc/internal/bundled/source/es2025.json.d.ts (+ generated lib/embed)
- reference from es2025 umbrella + libs.json + LibMap (enummaps.go)
- compiler test and baselines
Preserves RawJSON, JSON.rawJSON, JSON.isRawJSON, and optional
context.source on JSON.parse reviver.
Fixes #61330
Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
---
tsc/internal/bundled/embed_generated.go | 4 +
tsc/internal/bundled/libs/lib.es2025.d.ts | 1 +
.../bundled/libs/lib.es2025.json.d.ts | 54 ++++
tsc/internal/bundled/libs_generated.go | 1 +
tsc/internal/bundled/source/es2025.d.ts | 15 +-
tsc/internal/bundled/source/es2025.json.d.ts | 38 +++
tsc/internal/bundled/source/libs.json | 253 +++++++++---------
tsc/internal/tsoptions/enummaps.go | 1 +
.../reference/compiler/jsonParseWithSource.js | 69 +++++
.../compiler/jsonParseWithSource.symbols | 124 +++++++++
.../compiler/jsonParseWithSource.types | 230 ++++++++++++++++
.../cases/compiler/jsonParseWithSource.ts | 37 +++
12 files changed, 694 insertions(+), 133 deletions(-)
create mode 100644 tsc/internal/bundled/libs/lib.es2025.json.d.ts
create mode 100644 tsc/internal/bundled/source/es2025.json.d.ts
create mode 100644 tsc/testdata/baselines/reference/compiler/jsonParseWithSource.js
create mode 100644 tsc/testdata/baselines/reference/compiler/jsonParseWithSource.symbols
create mode 100644 tsc/testdata/baselines/reference/compiler/jsonParseWithSource.types
create mode 100644 tsc/testdata/tests/cases/compiler/jsonParseWithSource.ts
diff --git a/tsc/internal/bundled/embed_generated.go b/tsc/internal/bundled/embed_generated.go
index 53166f01f98d9..201f55bef264c 100644
--- a/tsc/internal/bundled/embed_generated.go
+++ b/tsc/internal/bundled/embed_generated.go
@@ -185,6 +185,8 @@ var (
libs_lib_es2025_intl_d_ts string
//go:embed libs/lib.es2025.iterator.d.ts
libs_lib_es2025_iterator_d_ts string
+ //go:embed libs/lib.es2025.json.d.ts
+ libs_lib_es2025_json_d_ts string
//go:embed libs/lib.es2025.promise.d.ts
libs_lib_es2025_promise_d_ts string
//go:embed libs/lib.es2025.regexp.d.ts
@@ -317,6 +319,7 @@ var embeddedContents = map[string]string{
"libs/lib.es2025.full.d.ts": libs_lib_es2025_full_d_ts,
"libs/lib.es2025.intl.d.ts": libs_lib_es2025_intl_d_ts,
"libs/lib.es2025.iterator.d.ts": libs_lib_es2025_iterator_d_ts,
+ "libs/lib.es2025.json.d.ts": libs_lib_es2025_json_d_ts,
"libs/lib.es2025.promise.d.ts": libs_lib_es2025_promise_d_ts,
"libs/lib.es2025.regexp.d.ts": libs_lib_es2025_regexp_d_ts,
"libs/lib.es5.d.ts": libs_lib_es5_d_ts,
@@ -428,6 +431,7 @@ var libsEntries = []fs.DirEntry{
&fileInfo{name: "lib.es2025.full.d.ts", size: int64(len(libs_lib_es2025_full_d_ts))},
&fileInfo{name: "lib.es2025.intl.d.ts", size: int64(len(libs_lib_es2025_intl_d_ts))},
&fileInfo{name: "lib.es2025.iterator.d.ts", size: int64(len(libs_lib_es2025_iterator_d_ts))},
+ &fileInfo{name: "lib.es2025.json.d.ts", size: int64(len(libs_lib_es2025_json_d_ts))},
&fileInfo{name: "lib.es2025.promise.d.ts", size: int64(len(libs_lib_es2025_promise_d_ts))},
&fileInfo{name: "lib.es2025.regexp.d.ts", size: int64(len(libs_lib_es2025_regexp_d_ts))},
&fileInfo{name: "lib.es5.d.ts", size: int64(len(libs_lib_es5_d_ts))},
diff --git a/tsc/internal/bundled/libs/lib.es2025.d.ts b/tsc/internal/bundled/libs/lib.es2025.d.ts
index 78e41e31e201f..9c30912092472 100644
--- a/tsc/internal/bundled/libs/lib.es2025.d.ts
+++ b/tsc/internal/bundled/libs/lib.es2025.d.ts
@@ -21,3 +21,4 @@ and limitations under the License.
///
///
///
+///
diff --git a/tsc/internal/bundled/libs/lib.es2025.json.d.ts b/tsc/internal/bundled/libs/lib.es2025.json.d.ts
new file mode 100644
index 0000000000000..b2bf1c6d3d539
--- /dev/null
+++ b/tsc/internal/bundled/libs/lib.es2025.json.d.ts
@@ -0,0 +1,54 @@
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABILITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+
+
+/**
+ * Represents a "raw JSON" object created by `JSON.rawJSON()`.
+ *
+ * Raw JSON objects are frozen, null-prototype objects that carry pre-serialized
+ * JSON text. When encountered by `JSON.stringify()`, the `rawJSON` property
+ * value is emitted verbatim instead of the usual serialization.
+ *
+ * @see {@link https://tc39.es/proposal-json-parse-with-source/ TC39 proposal-json-parse-with-source}
+ */
+interface RawJSON {
+ readonly rawJSON: string;
+}
+
+interface JSON {
+ /**
+ * Converts a JavaScript Object Notation (JSON) string into an object.
+ * @param text A valid JSON string.
+ * @param reviver A function that transforms the results. This function is called for each member of the object.
+ * If a member contains nested objects, the nested objects are transformed before the parent object is.
+ * For each value, the reviver also receives a `context` object. When the property is unmodified and its value is
+ * primitive, `context` has a `source` property containing the original JSON text of that value.
+ */
+ parse(text: string, reviver: (this: any, key: string, value: any, context: { source?: string }) => any): any;
+
+ /**
+ * Creates a "raw JSON" object containing a piece of JSON text.
+ * When serialized with `JSON.stringify()`, the raw text is emitted verbatim.
+ * @param text A valid JSON string representing a primitive value (string, number, boolean, or null).
+ * @throws {SyntaxError} If `text` is not valid JSON or represents an object or array.
+ */
+ rawJSON(text: string): RawJSON;
+
+ /**
+ * Returns whether the provided value is a raw JSON object created by `JSON.rawJSON()`.
+ * @param value The value to test.
+ */
+ isRawJSON(value: unknown): value is RawJSON;
+}
diff --git a/tsc/internal/bundled/libs_generated.go b/tsc/internal/bundled/libs_generated.go
index 6750307190f5b..8ce3c574e73ed 100644
--- a/tsc/internal/bundled/libs_generated.go
+++ b/tsc/internal/bundled/libs_generated.go
@@ -92,6 +92,7 @@ var LibNames = []string{
"lib.es2025.full.d.ts",
"lib.es2025.intl.d.ts",
"lib.es2025.iterator.d.ts",
+ "lib.es2025.json.d.ts",
"lib.es2025.promise.d.ts",
"lib.es2025.regexp.d.ts",
"lib.es5.d.ts",
diff --git a/tsc/internal/bundled/source/es2025.d.ts b/tsc/internal/bundled/source/es2025.d.ts
index 5a85bfba65b01..1ff1058893b9a 100644
--- a/tsc/internal/bundled/source/es2025.d.ts
+++ b/tsc/internal/bundled/source/es2025.d.ts
@@ -1,7 +1,8 @@
-///
-///
-///
-///
-///
-///
-///
+///
+///
+///
+///
+///
+///
+///
+///
diff --git a/tsc/internal/bundled/source/es2025.json.d.ts b/tsc/internal/bundled/source/es2025.json.d.ts
new file mode 100644
index 0000000000000..3241ada62cb6f
--- /dev/null
+++ b/tsc/internal/bundled/source/es2025.json.d.ts
@@ -0,0 +1,38 @@
+/**
+ * Represents a "raw JSON" object created by `JSON.rawJSON()`.
+ *
+ * Raw JSON objects are frozen, null-prototype objects that carry pre-serialized
+ * JSON text. When encountered by `JSON.stringify()`, the `rawJSON` property
+ * value is emitted verbatim instead of the usual serialization.
+ *
+ * @see {@link https://tc39.es/proposal-json-parse-with-source/ TC39 proposal-json-parse-with-source}
+ */
+interface RawJSON {
+ readonly rawJSON: string;
+}
+
+interface JSON {
+ /**
+ * Converts a JavaScript Object Notation (JSON) string into an object.
+ * @param text A valid JSON string.
+ * @param reviver A function that transforms the results. This function is called for each member of the object.
+ * If a member contains nested objects, the nested objects are transformed before the parent object is.
+ * For each value, the reviver also receives a `context` object. When the property is unmodified and its value is
+ * primitive, `context` has a `source` property containing the original JSON text of that value.
+ */
+ parse(text: string, reviver: (this: any, key: string, value: any, context: { source?: string }) => any): any;
+
+ /**
+ * Creates a "raw JSON" object containing a piece of JSON text.
+ * When serialized with `JSON.stringify()`, the raw text is emitted verbatim.
+ * @param text A valid JSON string representing a primitive value (string, number, boolean, or null).
+ * @throws {SyntaxError} If `text` is not valid JSON or represents an object or array.
+ */
+ rawJSON(text: string): RawJSON;
+
+ /**
+ * Returns whether the provided value is a raw JSON object created by `JSON.rawJSON()`.
+ * @param value The value to test.
+ */
+ isRawJSON(value: unknown): value is RawJSON;
+}
diff --git a/tsc/internal/bundled/source/libs.json b/tsc/internal/bundled/source/libs.json
index 5bc6a8b57a2c1..b1696846b858a 100644
--- a/tsc/internal/bundled/source/libs.json
+++ b/tsc/internal/bundled/source/libs.json
@@ -1,126 +1,127 @@
-{
- "libs": [
- // JavaScript only
- "es5",
- "es2015",
- "es2016",
- "es2017",
- "es2018",
- "es2019",
- "es2020",
- "es2021",
- "es2022",
- "es2023",
- "es2024",
- "es2025",
- "esnext",
- // Host only
- "dom.generated",
- "dom.iterable.generated",
- "dom.asynciterable.generated",
- "webworker.generated",
- "webworker.importscripts",
- "webworker.iterable.generated",
- "webworker.asynciterable.generated",
- "scripthost",
- // By-feature options
- "es2015.core",
- "es2015.collection",
- "es2015.generator",
- "es2015.iterable",
- "es2015.promise",
- "es2015.proxy",
- "es2015.reflect",
- "es2015.symbol",
- "es2015.symbol.wellknown",
- "es2016.array.include",
- "es2016.intl",
- "es2017.arraybuffer",
- "es2017.date",
- "es2017.object",
- "es2017.sharedmemory",
- "es2017.string",
- "es2017.intl",
- "es2017.typedarrays",
- "es2018.asyncgenerator",
- "es2018.asynciterable",
- "es2018.regexp",
- "es2018.promise",
- "es2018.intl",
- "es2019.array",
- "es2019.object",
- "es2019.string",
- "es2019.symbol",
- "es2019.intl",
- "es2020.bigint",
- "es2020.date",
- "es2020.promise",
- "es2020.sharedmemory",
- "es2020.string",
- "es2020.symbol.wellknown",
- "es2020.intl",
- "es2020.number",
- "es2021.string",
- "es2021.promise",
- "es2021.weakref",
- "es2021.intl",
- "es2022.array",
- "es2022.error",
- "es2022.intl",
- "es2022.object",
- "es2022.string",
- "es2022.regexp",
- "es2023.array",
- "es2023.collection",
- "es2023.intl",
- "es2024.arraybuffer",
- "es2024.collection",
- "es2024.object",
- "es2024.promise",
- "es2024.regexp",
- "es2024.sharedmemory",
- "es2024.string",
- "es2025.collection",
- "es2025.float16",
- "es2025.intl",
- "es2025.iterator",
- "es2025.promise",
- "es2025.regexp",
- "esnext.array",
- "esnext.collection",
- "esnext.date",
- "esnext.decorators",
- "esnext.disposable",
- "esnext.error",
- "esnext.intl",
- "esnext.sharedmemory",
- "esnext.temporal",
- "esnext.typedarrays",
- "decorators",
- "decorators.legacy",
- // Default libraries
- "es5.full",
- "es2015.full",
- "es2016.full",
- "es2017.full",
- "es2018.full",
- "es2019.full",
- "es2020.full",
- "es2021.full",
- "es2022.full",
- "es2023.full",
- "es2024.full",
- "es2025.full",
- "esnext.full"
- ],
- "paths": {
- "dom.generated": "lib.dom.d.ts",
- "dom.iterable.generated": "lib.dom.iterable.d.ts",
- "dom.asynciterable.generated": "lib.dom.asynciterable.d.ts",
- "webworker.generated": "lib.webworker.d.ts",
- "webworker.iterable.generated": "lib.webworker.iterable.d.ts",
- "webworker.asynciterable.generated": "lib.webworker.asynciterable.d.ts",
- "es5.full": "lib.d.ts",
- "es2015.full": "lib.es6.d.ts"
- }
-}
+{
+ "libs": [
+ // JavaScript only
+ "es5",
+ "es2015",
+ "es2016",
+ "es2017",
+ "es2018",
+ "es2019",
+ "es2020",
+ "es2021",
+ "es2022",
+ "es2023",
+ "es2024",
+ "es2025",
+ "esnext",
+ // Host only
+ "dom.generated",
+ "dom.iterable.generated",
+ "dom.asynciterable.generated",
+ "webworker.generated",
+ "webworker.importscripts",
+ "webworker.iterable.generated",
+ "webworker.asynciterable.generated",
+ "scripthost",
+ // By-feature options
+ "es2015.core",
+ "es2015.collection",
+ "es2015.generator",
+ "es2015.iterable",
+ "es2015.promise",
+ "es2015.proxy",
+ "es2015.reflect",
+ "es2015.symbol",
+ "es2015.symbol.wellknown",
+ "es2016.array.include",
+ "es2016.intl",
+ "es2017.arraybuffer",
+ "es2017.date",
+ "es2017.object",
+ "es2017.sharedmemory",
+ "es2017.string",
+ "es2017.intl",
+ "es2017.typedarrays",
+ "es2018.asyncgenerator",
+ "es2018.asynciterable",
+ "es2018.regexp",
+ "es2018.promise",
+ "es2018.intl",
+ "es2019.array",
+ "es2019.object",
+ "es2019.string",
+ "es2019.symbol",
+ "es2019.intl",
+ "es2020.bigint",
+ "es2020.date",
+ "es2020.promise",
+ "es2020.sharedmemory",
+ "es2020.string",
+ "es2020.symbol.wellknown",
+ "es2020.intl",
+ "es2020.number",
+ "es2021.string",
+ "es2021.promise",
+ "es2021.weakref",
+ "es2021.intl",
+ "es2022.array",
+ "es2022.error",
+ "es2022.intl",
+ "es2022.object",
+ "es2022.string",
+ "es2022.regexp",
+ "es2023.array",
+ "es2023.collection",
+ "es2023.intl",
+ "es2024.arraybuffer",
+ "es2024.collection",
+ "es2024.object",
+ "es2024.promise",
+ "es2024.regexp",
+ "es2024.sharedmemory",
+ "es2024.string",
+ "es2025.collection",
+ "es2025.float16",
+ "es2025.intl",
+ "es2025.iterator",
+ "es2025.promise",
+ "es2025.regexp",
+ "es2025.json",
+ "esnext.array",
+ "esnext.collection",
+ "esnext.date",
+ "esnext.decorators",
+ "esnext.disposable",
+ "esnext.error",
+ "esnext.intl",
+ "esnext.sharedmemory",
+ "esnext.temporal",
+ "esnext.typedarrays",
+ "decorators",
+ "decorators.legacy",
+ // Default libraries
+ "es5.full",
+ "es2015.full",
+ "es2016.full",
+ "es2017.full",
+ "es2018.full",
+ "es2019.full",
+ "es2020.full",
+ "es2021.full",
+ "es2022.full",
+ "es2023.full",
+ "es2024.full",
+ "es2025.full",
+ "esnext.full"
+ ],
+ "paths": {
+ "dom.generated": "lib.dom.d.ts",
+ "dom.iterable.generated": "lib.dom.iterable.d.ts",
+ "dom.asynciterable.generated": "lib.dom.asynciterable.d.ts",
+ "webworker.generated": "lib.webworker.d.ts",
+ "webworker.iterable.generated": "lib.webworker.iterable.d.ts",
+ "webworker.asynciterable.generated": "lib.webworker.asynciterable.d.ts",
+ "es5.full": "lib.d.ts",
+ "es2015.full": "lib.es6.d.ts"
+ }
+}
diff --git a/tsc/internal/tsoptions/enummaps.go b/tsc/internal/tsoptions/enummaps.go
index 3bf67a8640675..e5c68ab07ff1c 100644
--- a/tsc/internal/tsoptions/enummaps.go
+++ b/tsc/internal/tsoptions/enummaps.go
@@ -97,6 +97,7 @@ var LibMap = collections.NewOrderedMapFromList([]collections.MapEntry[string, an
{Key: "es2025.iterator", Value: "lib.es2025.iterator.d.ts"},
{Key: "es2025.promise", Value: "lib.es2025.promise.d.ts"},
{Key: "es2025.regexp", Value: "lib.es2025.regexp.d.ts"},
+ {Key: "es2025.json", Value: "lib.es2025.json.d.ts"},
// Fallback for backward compatibility
{Key: "esnext.asynciterable", Value: "lib.es2018.asynciterable.d.ts"},
{Key: "esnext.symbol", Value: "lib.es2019.symbol.d.ts"},
diff --git a/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.js b/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.js
new file mode 100644
index 0000000000000..8adfa2b928506
--- /dev/null
+++ b/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.js
@@ -0,0 +1,69 @@
+//// [tests/cases/compiler/jsonParseWithSource.ts] ////
+
+//// [jsonParseWithSource.ts]
+// JSON.rawJSON
+const raw = JSON.rawJSON("123");
+const rawStr: string = raw.rawJSON;
+JSON.stringify({ value: raw });
+JSON.stringify({ n: JSON.rawJSON("12345678901234567890") });
+
+// JSON.isRawJSON
+const maybeRaw: unknown = {};
+if (JSON.isRawJSON(maybeRaw)) {
+ const text: string = maybeRaw.rawJSON;
+}
+
+// JSON.parse with reviver context
+// `source` is optional: only present for unmodified primitive values
+JSON.parse('{"key":123}', (key, value, context) => {
+ const src: string | undefined = context.source;
+ return value;
+});
+
+// Test coverage for reviver context on array elements
+JSON.parse('[1, 2, 3]', (key, value, context) => {
+ const src: string | undefined = context.source;
+ return value;
+});
+
+// Test coverage for reviver context on nested objects
+JSON.parse('{"a": {"b": 1}}', (key, value, context) => {
+ const src: string | undefined = context.source;
+ return value;
+});
+
+// Existing JSON.parse overloads still work
+JSON.parse("{}");
+JSON.parse('{"a":1}', (key, value) => value);
+
+//// [jsonParseWithSource.js]
+"use strict";
+// JSON.rawJSON
+const raw = JSON.rawJSON("123");
+const rawStr = raw.rawJSON;
+JSON.stringify({ value: raw });
+JSON.stringify({ n: JSON.rawJSON("12345678901234567890") });
+// JSON.isRawJSON
+const maybeRaw = {};
+if (JSON.isRawJSON(maybeRaw)) {
+ const text = maybeRaw.rawJSON;
+}
+// JSON.parse with reviver context
+// `source` is optional: only present for unmodified primitive values
+JSON.parse('{"key":123}', (key, value, context) => {
+ const src = context.source;
+ return value;
+});
+// Test coverage for reviver context on array elements
+JSON.parse('[1, 2, 3]', (key, value, context) => {
+ const src = context.source;
+ return value;
+});
+// Test coverage for reviver context on nested objects
+JSON.parse('{"a": {"b": 1}}', (key, value, context) => {
+ const src = context.source;
+ return value;
+});
+// Existing JSON.parse overloads still work
+JSON.parse("{}");
+JSON.parse('{"a":1}', (key, value) => value);
diff --git a/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.symbols b/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.symbols
new file mode 100644
index 0000000000000..739c433365dd0
--- /dev/null
+++ b/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.symbols
@@ -0,0 +1,124 @@
+//// [tests/cases/compiler/jsonParseWithSource.ts] ////
+
+=== jsonParseWithSource.ts ===
+// JSON.rawJSON
+const raw = JSON.rawJSON("123");
+>raw : Symbol(raw, Decl(jsonParseWithSource.ts, 1, 5))
+>JSON.rawJSON : Symbol(JSON.rawJSON, Decl(lib.es2025.json.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>rawJSON : Symbol(JSON.rawJSON, Decl(lib.es2025.json.d.ts, --, --))
+
+const rawStr: string = raw.rawJSON;
+>rawStr : Symbol(rawStr, Decl(jsonParseWithSource.ts, 2, 5))
+>raw.rawJSON : Symbol(RawJSON.rawJSON, Decl(lib.es2025.json.d.ts, --, --))
+>raw : Symbol(raw, Decl(jsonParseWithSource.ts, 1, 5))
+>rawJSON : Symbol(RawJSON.rawJSON, Decl(lib.es2025.json.d.ts, --, --))
+
+JSON.stringify({ value: raw });
+>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 3, 16))
+>raw : Symbol(raw, Decl(jsonParseWithSource.ts, 1, 5))
+
+JSON.stringify({ n: JSON.rawJSON("12345678901234567890") });
+>JSON.stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>stringify : Symbol(JSON.stringify, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
+>n : Symbol(n, Decl(jsonParseWithSource.ts, 4, 16))
+>JSON.rawJSON : Symbol(JSON.rawJSON, Decl(lib.es2025.json.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>rawJSON : Symbol(JSON.rawJSON, Decl(lib.es2025.json.d.ts, --, --))
+
+// JSON.isRawJSON
+const maybeRaw: unknown = {};
+>maybeRaw : Symbol(maybeRaw, Decl(jsonParseWithSource.ts, 7, 5))
+
+if (JSON.isRawJSON(maybeRaw)) {
+>JSON.isRawJSON : Symbol(JSON.isRawJSON, Decl(lib.es2025.json.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>isRawJSON : Symbol(JSON.isRawJSON, Decl(lib.es2025.json.d.ts, --, --))
+>maybeRaw : Symbol(maybeRaw, Decl(jsonParseWithSource.ts, 7, 5))
+
+ const text: string = maybeRaw.rawJSON;
+>text : Symbol(text, Decl(jsonParseWithSource.ts, 9, 9))
+>maybeRaw.rawJSON : Symbol(RawJSON.rawJSON, Decl(lib.es2025.json.d.ts, --, --))
+>maybeRaw : Symbol(maybeRaw, Decl(jsonParseWithSource.ts, 7, 5))
+>rawJSON : Symbol(RawJSON.rawJSON, Decl(lib.es2025.json.d.ts, --, --))
+}
+
+// JSON.parse with reviver context
+// `source` is optional: only present for unmodified primitive values
+JSON.parse('{"key":123}', (key, value, context) => {
+>JSON.parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>key : Symbol(key, Decl(jsonParseWithSource.ts, 14, 27))
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 14, 31))
+>context : Symbol(context, Decl(jsonParseWithSource.ts, 14, 38))
+
+ const src: string | undefined = context.source;
+>src : Symbol(src, Decl(jsonParseWithSource.ts, 15, 9))
+>context.source : Symbol(source, Decl(lib.es2025.json.d.ts, --, --))
+>context : Symbol(context, Decl(jsonParseWithSource.ts, 14, 38))
+>source : Symbol(source, Decl(lib.es2025.json.d.ts, --, --))
+
+ return value;
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 14, 31))
+
+});
+
+// Test coverage for reviver context on array elements
+JSON.parse('[1, 2, 3]', (key, value, context) => {
+>JSON.parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>key : Symbol(key, Decl(jsonParseWithSource.ts, 20, 25))
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 20, 29))
+>context : Symbol(context, Decl(jsonParseWithSource.ts, 20, 36))
+
+ const src: string | undefined = context.source;
+>src : Symbol(src, Decl(jsonParseWithSource.ts, 21, 9))
+>context.source : Symbol(source, Decl(lib.es2025.json.d.ts, --, --))
+>context : Symbol(context, Decl(jsonParseWithSource.ts, 20, 36))
+>source : Symbol(source, Decl(lib.es2025.json.d.ts, --, --))
+
+ return value;
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 20, 29))
+
+});
+
+// Test coverage for reviver context on nested objects
+JSON.parse('{"a": {"b": 1}}', (key, value, context) => {
+>JSON.parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>key : Symbol(key, Decl(jsonParseWithSource.ts, 26, 31))
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 26, 35))
+>context : Symbol(context, Decl(jsonParseWithSource.ts, 26, 42))
+
+ const src: string | undefined = context.source;
+>src : Symbol(src, Decl(jsonParseWithSource.ts, 27, 9))
+>context.source : Symbol(source, Decl(lib.es2025.json.d.ts, --, --))
+>context : Symbol(context, Decl(jsonParseWithSource.ts, 26, 42))
+>source : Symbol(source, Decl(lib.es2025.json.d.ts, --, --))
+
+ return value;
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 26, 35))
+
+});
+
+// Existing JSON.parse overloads still work
+JSON.parse("{}");
+>JSON.parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+
+JSON.parse('{"a":1}', (key, value) => value);
+>JSON.parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --), Decl(lib.es2025.json.d.ts, --, --))
+>key : Symbol(key, Decl(jsonParseWithSource.ts, 33, 23))
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 33, 27))
+>value : Symbol(value, Decl(jsonParseWithSource.ts, 33, 27))
+
diff --git a/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.types b/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.types
new file mode 100644
index 0000000000000..c97ff69701786
--- /dev/null
+++ b/tsc/testdata/baselines/reference/compiler/jsonParseWithSource.types
@@ -0,0 +1,230 @@
+//// [tests/cases/compiler/jsonParseWithSource.ts] ////
+
+=== jsonParseWithSource.ts ===
+// JSON.rawJSON
+const raw = JSON.rawJSON("123");
+>raw : RawJSON
+> : ^^^^^^^
+>JSON.rawJSON("123") : RawJSON
+> : ^^^^^^^
+>JSON.rawJSON : (text: string) => RawJSON
+> : ^ ^^ ^^^^^
+>JSON : JSON
+> : ^^^^
+>rawJSON : (text: string) => RawJSON
+> : ^ ^^ ^^^^^
+>"123" : "123"
+> : ^^^^^
+
+const rawStr: string = raw.rawJSON;
+>rawStr : string
+> : ^^^^^^
+>raw.rawJSON : string
+> : ^^^^^^
+>raw : RawJSON
+> : ^^^^^^^
+>rawJSON : string
+> : ^^^^^^
+
+JSON.stringify({ value: raw });
+>JSON.stringify({ value: raw }) : string
+> : ^^^^^^
+>JSON.stringify : { (value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[] | null, space?: string | number): string; }
+> : ^^^ ^^ ^^ ^^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^ ^^^ ^^^ ^^^
+>JSON : JSON
+> : ^^^^
+>stringify : { (value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[] | null, space?: string | number): string; }
+> : ^^^ ^^ ^^ ^^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^ ^^^ ^^^ ^^^
+>{ value: raw } : { value: RawJSON; }
+> : ^^^^^^^^^^^^^^^^^^^
+>value : RawJSON
+> : ^^^^^^^
+>raw : RawJSON
+> : ^^^^^^^
+
+JSON.stringify({ n: JSON.rawJSON("12345678901234567890") });
+>JSON.stringify({ n: JSON.rawJSON("12345678901234567890") }) : string
+> : ^^^^^^
+>JSON.stringify : { (value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[] | null, space?: string | number): string; }
+> : ^^^ ^^ ^^ ^^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^ ^^^ ^^^ ^^^
+>JSON : JSON
+> : ^^^^
+>stringify : { (value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[] | null, space?: string | number): string; }
+> : ^^^ ^^ ^^ ^^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^^ ^^ ^^^ ^^^ ^^^
+>{ n: JSON.rawJSON("12345678901234567890") } : { n: RawJSON; }
+> : ^^^^^^^^^^^^^^^
+>n : RawJSON
+> : ^^^^^^^
+>JSON.rawJSON("12345678901234567890") : RawJSON
+> : ^^^^^^^
+>JSON.rawJSON : (text: string) => RawJSON
+> : ^ ^^ ^^^^^
+>JSON : JSON
+> : ^^^^
+>rawJSON : (text: string) => RawJSON
+> : ^ ^^ ^^^^^
+>"12345678901234567890" : "12345678901234567890"
+> : ^^^^^^^^^^^^^^^^^^^^^^
+
+// JSON.isRawJSON
+const maybeRaw: unknown = {};
+>maybeRaw : unknown
+> : ^^^^^^^
+>{} : {}
+> : ^^
+
+if (JSON.isRawJSON(maybeRaw)) {
+>JSON.isRawJSON(maybeRaw) : boolean
+> : ^^^^^^^
+>JSON.isRawJSON : (value: unknown) => value is RawJSON
+> : ^ ^^ ^^^^^
+>JSON : JSON
+> : ^^^^
+>isRawJSON : (value: unknown) => value is RawJSON
+> : ^ ^^ ^^^^^
+>maybeRaw : unknown
+> : ^^^^^^^
+
+ const text: string = maybeRaw.rawJSON;
+>text : string
+> : ^^^^^^
+>maybeRaw.rawJSON : string
+> : ^^^^^^
+>maybeRaw : RawJSON
+> : ^^^^^^^
+>rawJSON : string
+> : ^^^^^^
+}
+
+// JSON.parse with reviver context
+// `source` is optional: only present for unmodified primitive values
+JSON.parse('{"key":123}', (key, value, context) => {
+>JSON.parse('{"key":123}', (key, value, context) => { const src: string | undefined = context.source; return value;}) : any
+>JSON.parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>JSON : JSON
+> : ^^^^
+>parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>'{"key":123}' : "{\"key\":123}"
+> : ^^^^^^^^^^^^^^^
+>(key, value, context) => { const src: string | undefined = context.source; return value;} : (this: any, key: string, value: any, context: { source?: string; }) => any
+> : ^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^
+>key : string
+> : ^^^^^^
+>value : any
+>context : { source?: string; }
+> : ^^^^^^^^^^^ ^^^
+
+ const src: string | undefined = context.source;
+>src : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+>context.source : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+>context : { source?: string; }
+> : ^^^^^^^^^^^ ^^^
+>source : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+
+ return value;
+>value : any
+
+});
+
+// Test coverage for reviver context on array elements
+JSON.parse('[1, 2, 3]', (key, value, context) => {
+>JSON.parse('[1, 2, 3]', (key, value, context) => { const src: string | undefined = context.source; return value;}) : any
+>JSON.parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>JSON : JSON
+> : ^^^^
+>parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>'[1, 2, 3]' : "[1, 2, 3]"
+> : ^^^^^^^^^^^
+>(key, value, context) => { const src: string | undefined = context.source; return value;} : (this: any, key: string, value: any, context: { source?: string; }) => any
+> : ^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^
+>key : string
+> : ^^^^^^
+>value : any
+>context : { source?: string; }
+> : ^^^^^^^^^^^ ^^^
+
+ const src: string | undefined = context.source;
+>src : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+>context.source : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+>context : { source?: string; }
+> : ^^^^^^^^^^^ ^^^
+>source : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+
+ return value;
+>value : any
+
+});
+
+// Test coverage for reviver context on nested objects
+JSON.parse('{"a": {"b": 1}}', (key, value, context) => {
+>JSON.parse('{"a": {"b": 1}}', (key, value, context) => { const src: string | undefined = context.source; return value;}) : any
+>JSON.parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>JSON : JSON
+> : ^^^^
+>parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>'{"a": {"b": 1}}' : "{\"a\": {\"b\": 1}}"
+> : ^^^^^^^^^^^^^^^^^^^^^
+>(key, value, context) => { const src: string | undefined = context.source; return value;} : (this: any, key: string, value: any, context: { source?: string; }) => any
+> : ^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^
+>key : string
+> : ^^^^^^
+>value : any
+>context : { source?: string; }
+> : ^^^^^^^^^^^ ^^^
+
+ const src: string | undefined = context.source;
+>src : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+>context.source : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+>context : { source?: string; }
+> : ^^^^^^^^^^^ ^^^
+>source : string | undefined
+> : ^^^^^^^^^^^^^^^^^^
+
+ return value;
+>value : any
+
+});
+
+// Existing JSON.parse overloads still work
+JSON.parse("{}");
+>JSON.parse("{}") : any
+>JSON.parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>JSON : JSON
+> : ^^^^
+>parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>"{}" : "{}"
+> : ^^^^
+
+JSON.parse('{"a":1}', (key, value) => value);
+>JSON.parse('{"a":1}', (key, value) => value) : any
+>JSON.parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>JSON : JSON
+> : ^^^^
+>parse : { (text: string, reviver?: (this: any, key: string, value: any) => any): any; (text: string, reviver: (this: any, key: string, value: any, context: { source?: string; }) => any): any; }
+> : ^^^ ^^ ^^ ^^^ ^^^ ^^^ ^^ ^^ ^^ ^^^ ^^^
+>'{"a":1}' : "{\"a\":1}"
+> : ^^^^^^^^^^^
+>(key, value) => value : (this: any, key: string, value: any) => any
+> : ^ ^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^^^^
+>key : string
+> : ^^^^^^
+>value : any
+>value : any
+
diff --git a/tsc/testdata/tests/cases/compiler/jsonParseWithSource.ts b/tsc/testdata/tests/cases/compiler/jsonParseWithSource.ts
new file mode 100644
index 0000000000000..3dc5b3761eb36
--- /dev/null
+++ b/tsc/testdata/tests/cases/compiler/jsonParseWithSource.ts
@@ -0,0 +1,37 @@
+// @lib: es2025
+// @strictNullChecks: true
+
+// JSON.rawJSON
+const raw = JSON.rawJSON("123");
+const rawStr: string = raw.rawJSON;
+JSON.stringify({ value: raw });
+JSON.stringify({ n: JSON.rawJSON("12345678901234567890") });
+
+// JSON.isRawJSON
+const maybeRaw: unknown = {};
+if (JSON.isRawJSON(maybeRaw)) {
+ const text: string = maybeRaw.rawJSON;
+}
+
+// JSON.parse with reviver context
+// `source` is optional: only present for unmodified primitive values
+JSON.parse('{"key":123}', (key, value, context) => {
+ const src: string | undefined = context.source;
+ return value;
+});
+
+// Test coverage for reviver context on array elements
+JSON.parse('[1, 2, 3]', (key, value, context) => {
+ const src: string | undefined = context.source;
+ return value;
+});
+
+// Test coverage for reviver context on nested objects
+JSON.parse('{"a": {"b": 1}}', (key, value, context) => {
+ const src: string | undefined = context.source;
+ return value;
+});
+
+// Existing JSON.parse overloads still work
+JSON.parse("{}");
+JSON.parse('{"a":1}', (key, value) => value);