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);