From c5b1deec5a1086618a9735cf35fe0ce9494794c4 Mon Sep 17 00:00:00 2001 From: James Prevett Date: Sat, 17 Jan 2026 18:26:30 -0600 Subject: [PATCH 1/3] Added constant JSON imports and `importJsonAsConst` --- src/compiler/checker.ts | 6 ++++++ src/compiler/commandLineParser.ts | 9 +++++++++ src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/types.ts | 1 + 4 files changed, 20 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 390c843b0c968..7281325e7f281 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12515,6 +12515,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!declaration.statements.length) { return emptyObjectType; } + if (compilerOptions.importJsonAsConst) { + return checkExpression(declaration.statements[0].expression); + } return getWidenedType(getWidenedLiteralType(checkExpression(declaration.statements[0].expression))); } if (isAccessor(declaration)) { @@ -41395,6 +41398,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function isConstContext(node: Expression): boolean { const parent = node.parent; + if (compilerOptions.importJsonAsConst && parent.kind === SyntaxKind.ExpressionStatement && isSourceFile(parent.parent) && isJsonSourceFile(parent.parent)) { + return true; + } return isAssertionExpression(parent) && isConstTypeReference(parent.type) || isJSDocTypeAssertion(parent) && isConstTypeReference(getJSDocTypeAssertionType(parent)) || isValidConstAssertionArgument(node) && isConstTypeVariable(getContextualType(node, ContextFlags.None)) || diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 236e643b00b3c..1f6a7bb29ed38 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1343,6 +1343,15 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [ description: Diagnostics.Enable_importing_json_files, defaultValueDescription: false, }, + { + name: "importJsonAsConst", + type: "boolean", + affectsSemanticDiagnostics: true, + affectsBuildInfo: true, + category: Diagnostics.Language_and_Environment, + description: Diagnostics.Import_JSON_files_as_const_assertions, + defaultValueDescription: false, + }, { name: "allowArbitraryExtensions", type: "boolean", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1a69dba02a6c1..04158ab6957cb 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -6719,6 +6719,10 @@ "category": "Message", "code": 6932 }, + "Import JSON files as const assertions.": { + "category": "Message", + "code": 6933 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4e9f872720d79..3bd70221d1b4c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7442,6 +7442,7 @@ export interface CompilerOptions { /** @internal */ help?: boolean; ignoreDeprecations?: string; importHelpers?: boolean; + importJsonAsConst?: boolean; /** @deprecated */ importsNotUsedAsValues?: ImportsNotUsedAsValues; /** @internal */ init?: boolean; From 42baf326a0d79e39ac391e904fb34dd4db5079ec Mon Sep 17 00:00:00 2001 From: James Prevett Date: Sat, 17 Jan 2026 18:26:47 -0600 Subject: [PATCH 2/3] Added tests for constant JSON imports --- tests/baselines/reference/jsonLiteralTypes.js | 30 ++++ .../reference/jsonLiteralTypes.symbols | 45 ++++++ .../reference/jsonLiteralTypes.types | 83 +++++++++++ .../jsonLiteralTypesDefault.errors.txt | 43 ++++++ .../reference/jsonLiteralTypesDefault.js | 50 +++++++ .../reference/jsonLiteralTypesDefault.symbols | 75 ++++++++++ .../reference/jsonLiteralTypesDefault.types | 129 ++++++++++++++++++ tests/cases/compiler/jsonLiteralTypes.ts | 22 +++ .../cases/compiler/jsonLiteralTypesDefault.ts | 32 +++++ 9 files changed, 509 insertions(+) create mode 100644 tests/baselines/reference/jsonLiteralTypes.js create mode 100644 tests/baselines/reference/jsonLiteralTypes.symbols create mode 100644 tests/baselines/reference/jsonLiteralTypes.types create mode 100644 tests/baselines/reference/jsonLiteralTypesDefault.errors.txt create mode 100644 tests/baselines/reference/jsonLiteralTypesDefault.js create mode 100644 tests/baselines/reference/jsonLiteralTypesDefault.symbols create mode 100644 tests/baselines/reference/jsonLiteralTypesDefault.types create mode 100644 tests/cases/compiler/jsonLiteralTypes.ts create mode 100644 tests/cases/compiler/jsonLiteralTypesDefault.ts diff --git a/tests/baselines/reference/jsonLiteralTypes.js b/tests/baselines/reference/jsonLiteralTypes.js new file mode 100644 index 0000000000000..71af9d0cd905c --- /dev/null +++ b/tests/baselines/reference/jsonLiteralTypes.js @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/jsonLiteralTypes.ts] //// + +//// [data.json] +{ + "s": "string literal", + "n": 123, + "b": true, + "arr": ["a", "b"] +} + +//// [main.ts] +import data from "./data.json"; + +const s: "string literal" = data.s; +const n: 123 = data.n; +const b: true = data.b; +const arr: readonly ["a", "b"] = data.arr; + + +//// [main.js] +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const data_json_1 = __importDefault(require("./data.json")); +const s = data_json_1.default.s; +const n = data_json_1.default.n; +const b = data_json_1.default.b; +const arr = data_json_1.default.arr; diff --git a/tests/baselines/reference/jsonLiteralTypes.symbols b/tests/baselines/reference/jsonLiteralTypes.symbols new file mode 100644 index 0000000000000..8bd88e8d7046d --- /dev/null +++ b/tests/baselines/reference/jsonLiteralTypes.symbols @@ -0,0 +1,45 @@ +//// [tests/cases/compiler/jsonLiteralTypes.ts] //// + +=== data.json === +{ + "s": "string literal", +>"s" : Symbol("s", Decl(data.json, 0, 1)) + + "n": 123, +>"n" : Symbol("n", Decl(data.json, 1, 26)) + + "b": true, +>"b" : Symbol("b", Decl(data.json, 2, 13)) + + "arr": ["a", "b"] +>"arr" : Symbol("arr", Decl(data.json, 3, 14)) +} + +=== main.ts === +import data from "./data.json"; +>data : Symbol(data, Decl(main.ts, 0, 6)) + +const s: "string literal" = data.s; +>s : Symbol(s, Decl(main.ts, 2, 5)) +>data.s : Symbol("s", Decl(data.json, 0, 1)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>s : Symbol("s", Decl(data.json, 0, 1)) + +const n: 123 = data.n; +>n : Symbol(n, Decl(main.ts, 3, 5)) +>data.n : Symbol("n", Decl(data.json, 1, 26)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>n : Symbol("n", Decl(data.json, 1, 26)) + +const b: true = data.b; +>b : Symbol(b, Decl(main.ts, 4, 5)) +>data.b : Symbol("b", Decl(data.json, 2, 13)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>b : Symbol("b", Decl(data.json, 2, 13)) + +const arr: readonly ["a", "b"] = data.arr; +>arr : Symbol(arr, Decl(main.ts, 5, 5)) +>data.arr : Symbol("arr", Decl(data.json, 3, 14)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>arr : Symbol("arr", Decl(data.json, 3, 14)) + diff --git a/tests/baselines/reference/jsonLiteralTypes.types b/tests/baselines/reference/jsonLiteralTypes.types new file mode 100644 index 0000000000000..b0da11331bce6 --- /dev/null +++ b/tests/baselines/reference/jsonLiteralTypes.types @@ -0,0 +1,83 @@ +//// [tests/cases/compiler/jsonLiteralTypes.ts] //// + +=== data.json === +{ +>{ "s": "string literal", "n": 123, "b": true, "arr": ["a", "b"]} : { readonly s: "string literal"; readonly n: 123; readonly b: true; readonly arr: readonly ["a", "b"]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + "s": "string literal", +>"s" : "string literal" +> : ^^^^^^^^^^^^^^^^ +>"string literal" : "string literal" +> : ^^^^^^^^^^^^^^^^ + + "n": 123, +>"n" : 123 +> : ^^^ +>123 : 123 +> : ^^^ + + "b": true, +>"b" : true +> : ^^^^ +>true : true +> : ^^^^ + + "arr": ["a", "b"] +>"arr" : readonly ["a", "b"] +> : ^^^^^^^^^^^^^^^^^^^ +>["a", "b"] : readonly ["a", "b"] +> : ^^^^^^^^^^^^^^^^^^^ +>"a" : "a" +> : ^^^ +>"b" : "b" +> : ^^^ +} + +=== main.ts === +import data from "./data.json"; +>data : { readonly s: "string literal"; readonly n: 123; readonly b: true; readonly arr: readonly ["a", "b"]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +const s: "string literal" = data.s; +>s : "string literal" +> : ^^^^^^^^^^^^^^^^ +>data.s : "string literal" +> : ^^^^^^^^^^^^^^^^ +>data : { readonly s: "string literal"; readonly n: 123; readonly b: true; readonly arr: readonly ["a", "b"]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>s : "string literal" +> : ^^^^^^^^^^^^^^^^ + +const n: 123 = data.n; +>n : 123 +> : ^^^ +>data.n : 123 +> : ^^^ +>data : { readonly s: "string literal"; readonly n: 123; readonly b: true; readonly arr: readonly ["a", "b"]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>n : 123 +> : ^^^ + +const b: true = data.b; +>b : true +> : ^^^^ +>true : true +> : ^^^^ +>data.b : true +> : ^^^^ +>data : { readonly s: "string literal"; readonly n: 123; readonly b: true; readonly arr: readonly ["a", "b"]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>b : true +> : ^^^^ + +const arr: readonly ["a", "b"] = data.arr; +>arr : readonly ["a", "b"] +> : ^^^^^^^^^^^^^^^^^^^ +>data.arr : readonly ["a", "b"] +> : ^^^^^^^^^^^^^^^^^^^ +>data : { readonly s: "string literal"; readonly n: 123; readonly b: true; readonly arr: readonly ["a", "b"]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>arr : readonly ["a", "b"] +> : ^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/baselines/reference/jsonLiteralTypesDefault.errors.txt b/tests/baselines/reference/jsonLiteralTypesDefault.errors.txt new file mode 100644 index 0000000000000..2b1cdc71e5cbd --- /dev/null +++ b/tests/baselines/reference/jsonLiteralTypesDefault.errors.txt @@ -0,0 +1,43 @@ +main.ts(14,7): error TS2322: Type 'string' is not assignable to type '"string literal"'. +main.ts(15,7): error TS2322: Type 'number' is not assignable to type '123'. +main.ts(16,7): error TS2322: Type 'boolean' is not assignable to type 'true'. +main.ts(17,7): error TS2322: Type 'string[]' is not assignable to type 'readonly ["a", "b"]'. + Target requires 2 element(s) but source may have fewer. + + +==== data.json (0 errors) ==== + { + "s": "string literal", + "n": 123, + "b": true, + "arr": ["a", "b"] + } + +==== main.ts (4 errors) ==== + import data from "./data.json"; + + // Should be wide types + const s: string = data.s; + const n: number = data.n; + const b: boolean = data.b; + const arr: string[] = data.arr; + + // Should NOT be literal types (these assignments should fail if they were literals, but since we are assigning TO them, we check what they ARE) + // Actually, data.s is string. So `const x: "literal" = data.s` would fail if data.s is string. + // But here data.s IS string. + // Let's verify by assigning to literals which should fail if it is string. + + const literalS: "string literal" = data.s; // Error expected: string not assignable to "string literal" + ~~~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type '"string literal"'. + const literalN: 123 = data.n; // Error expected + ~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type '123'. + const literalB: true = data.b; // Error expected + ~~~~~~~~ +!!! error TS2322: Type 'boolean' is not assignable to type 'true'. + const literalArr: readonly ["a", "b"] = data.arr; // Error expected + ~~~~~~~~~~ +!!! error TS2322: Type 'string[]' is not assignable to type 'readonly ["a", "b"]'. +!!! error TS2322: Target requires 2 element(s) but source may have fewer. + \ No newline at end of file diff --git a/tests/baselines/reference/jsonLiteralTypesDefault.js b/tests/baselines/reference/jsonLiteralTypesDefault.js new file mode 100644 index 0000000000000..a1199de8feee1 --- /dev/null +++ b/tests/baselines/reference/jsonLiteralTypesDefault.js @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/jsonLiteralTypesDefault.ts] //// + +//// [data.json] +{ + "s": "string literal", + "n": 123, + "b": true, + "arr": ["a", "b"] +} + +//// [main.ts] +import data from "./data.json"; + +// Should be wide types +const s: string = data.s; +const n: number = data.n; +const b: boolean = data.b; +const arr: string[] = data.arr; + +// Should NOT be literal types (these assignments should fail if they were literals, but since we are assigning TO them, we check what they ARE) +// Actually, data.s is string. So `const x: "literal" = data.s` would fail if data.s is string. +// But here data.s IS string. +// Let's verify by assigning to literals which should fail if it is string. + +const literalS: "string literal" = data.s; // Error expected: string not assignable to "string literal" +const literalN: 123 = data.n; // Error expected +const literalB: true = data.b; // Error expected +const literalArr: readonly ["a", "b"] = data.arr; // Error expected + + +//// [main.js] +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const data_json_1 = __importDefault(require("./data.json")); +// Should be wide types +const s = data_json_1.default.s; +const n = data_json_1.default.n; +const b = data_json_1.default.b; +const arr = data_json_1.default.arr; +// Should NOT be literal types (these assignments should fail if they were literals, but since we are assigning TO them, we check what they ARE) +// Actually, data.s is string. So `const x: "literal" = data.s` would fail if data.s is string. +// But here data.s IS string. +// Let's verify by assigning to literals which should fail if it is string. +const literalS = data_json_1.default.s; // Error expected: string not assignable to "string literal" +const literalN = data_json_1.default.n; // Error expected +const literalB = data_json_1.default.b; // Error expected +const literalArr = data_json_1.default.arr; // Error expected diff --git a/tests/baselines/reference/jsonLiteralTypesDefault.symbols b/tests/baselines/reference/jsonLiteralTypesDefault.symbols new file mode 100644 index 0000000000000..1650f33489b69 --- /dev/null +++ b/tests/baselines/reference/jsonLiteralTypesDefault.symbols @@ -0,0 +1,75 @@ +//// [tests/cases/compiler/jsonLiteralTypesDefault.ts] //// + +=== data.json === +{ + "s": "string literal", +>"s" : Symbol("s", Decl(data.json, 0, 1)) + + "n": 123, +>"n" : Symbol("n", Decl(data.json, 1, 26)) + + "b": true, +>"b" : Symbol("b", Decl(data.json, 2, 13)) + + "arr": ["a", "b"] +>"arr" : Symbol("arr", Decl(data.json, 3, 14)) +} + +=== main.ts === +import data from "./data.json"; +>data : Symbol(data, Decl(main.ts, 0, 6)) + +// Should be wide types +const s: string = data.s; +>s : Symbol(s, Decl(main.ts, 3, 5)) +>data.s : Symbol("s", Decl(data.json, 0, 1)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>s : Symbol("s", Decl(data.json, 0, 1)) + +const n: number = data.n; +>n : Symbol(n, Decl(main.ts, 4, 5)) +>data.n : Symbol("n", Decl(data.json, 1, 26)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>n : Symbol("n", Decl(data.json, 1, 26)) + +const b: boolean = data.b; +>b : Symbol(b, Decl(main.ts, 5, 5)) +>data.b : Symbol("b", Decl(data.json, 2, 13)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>b : Symbol("b", Decl(data.json, 2, 13)) + +const arr: string[] = data.arr; +>arr : Symbol(arr, Decl(main.ts, 6, 5)) +>data.arr : Symbol("arr", Decl(data.json, 3, 14)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>arr : Symbol("arr", Decl(data.json, 3, 14)) + +// Should NOT be literal types (these assignments should fail if they were literals, but since we are assigning TO them, we check what they ARE) +// Actually, data.s is string. So `const x: "literal" = data.s` would fail if data.s is string. +// But here data.s IS string. +// Let's verify by assigning to literals which should fail if it is string. + +const literalS: "string literal" = data.s; // Error expected: string not assignable to "string literal" +>literalS : Symbol(literalS, Decl(main.ts, 13, 5)) +>data.s : Symbol("s", Decl(data.json, 0, 1)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>s : Symbol("s", Decl(data.json, 0, 1)) + +const literalN: 123 = data.n; // Error expected +>literalN : Symbol(literalN, Decl(main.ts, 14, 5)) +>data.n : Symbol("n", Decl(data.json, 1, 26)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>n : Symbol("n", Decl(data.json, 1, 26)) + +const literalB: true = data.b; // Error expected +>literalB : Symbol(literalB, Decl(main.ts, 15, 5)) +>data.b : Symbol("b", Decl(data.json, 2, 13)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>b : Symbol("b", Decl(data.json, 2, 13)) + +const literalArr: readonly ["a", "b"] = data.arr; // Error expected +>literalArr : Symbol(literalArr, Decl(main.ts, 16, 5)) +>data.arr : Symbol("arr", Decl(data.json, 3, 14)) +>data : Symbol(data, Decl(main.ts, 0, 6)) +>arr : Symbol("arr", Decl(data.json, 3, 14)) + diff --git a/tests/baselines/reference/jsonLiteralTypesDefault.types b/tests/baselines/reference/jsonLiteralTypesDefault.types new file mode 100644 index 0000000000000..56bff4c4e1f4f --- /dev/null +++ b/tests/baselines/reference/jsonLiteralTypesDefault.types @@ -0,0 +1,129 @@ +//// [tests/cases/compiler/jsonLiteralTypesDefault.ts] //// + +=== data.json === +{ +>{ "s": "string literal", "n": 123, "b": true, "arr": ["a", "b"]} : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + "s": "string literal", +>"s" : string +> : ^^^^^^ +>"string literal" : "string literal" +> : ^^^^^^^^^^^^^^^^ + + "n": 123, +>"n" : number +> : ^^^^^^ +>123 : 123 +> : ^^^ + + "b": true, +>"b" : boolean +> : ^^^^^^^ +>true : true +> : ^^^^ + + "arr": ["a", "b"] +>"arr" : string[] +> : ^^^^^^^^ +>["a", "b"] : string[] +> : ^^^^^^^^ +>"a" : "a" +> : ^^^ +>"b" : "b" +> : ^^^ +} + +=== main.ts === +import data from "./data.json"; +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Should be wide types +const s: string = data.s; +>s : string +> : ^^^^^^ +>data.s : string +> : ^^^^^^ +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>s : string +> : ^^^^^^ + +const n: number = data.n; +>n : number +> : ^^^^^^ +>data.n : number +> : ^^^^^^ +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>n : number +> : ^^^^^^ + +const b: boolean = data.b; +>b : boolean +> : ^^^^^^^ +>data.b : boolean +> : ^^^^^^^ +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>b : boolean +> : ^^^^^^^ + +const arr: string[] = data.arr; +>arr : string[] +> : ^^^^^^^^ +>data.arr : string[] +> : ^^^^^^^^ +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>arr : string[] +> : ^^^^^^^^ + +// Should NOT be literal types (these assignments should fail if they were literals, but since we are assigning TO them, we check what they ARE) +// Actually, data.s is string. So `const x: "literal" = data.s` would fail if data.s is string. +// But here data.s IS string. +// Let's verify by assigning to literals which should fail if it is string. + +const literalS: "string literal" = data.s; // Error expected: string not assignable to "string literal" +>literalS : "string literal" +> : ^^^^^^^^^^^^^^^^ +>data.s : string +> : ^^^^^^ +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>s : string +> : ^^^^^^ + +const literalN: 123 = data.n; // Error expected +>literalN : 123 +> : ^^^ +>data.n : number +> : ^^^^^^ +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>n : number +> : ^^^^^^ + +const literalB: true = data.b; // Error expected +>literalB : true +> : ^^^^ +>true : true +> : ^^^^ +>data.b : boolean +> : ^^^^^^^ +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>b : boolean +> : ^^^^^^^ + +const literalArr: readonly ["a", "b"] = data.arr; // Error expected +>literalArr : readonly ["a", "b"] +> : ^^^^^^^^^^^^^^^^^^^ +>data.arr : string[] +> : ^^^^^^^^ +>data : { s: string; n: number; b: boolean; arr: string[]; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>arr : string[] +> : ^^^^^^^^ + diff --git a/tests/cases/compiler/jsonLiteralTypes.ts b/tests/cases/compiler/jsonLiteralTypes.ts new file mode 100644 index 0000000000000..271bedb622225 --- /dev/null +++ b/tests/cases/compiler/jsonLiteralTypes.ts @@ -0,0 +1,22 @@ +// @resolveJsonModule: true +// @esModuleInterop: true +// @module: commonjs +// @target: esnext +// @strict: true +// @importJsonAsConst: true + +// @Filename: data.json +{ + "s": "string literal", + "n": 123, + "b": true, + "arr": ["a", "b"] +} + +// @Filename: main.ts +import data from "./data.json"; + +const s: "string literal" = data.s; +const n: 123 = data.n; +const b: true = data.b; +const arr: readonly ["a", "b"] = data.arr; diff --git a/tests/cases/compiler/jsonLiteralTypesDefault.ts b/tests/cases/compiler/jsonLiteralTypesDefault.ts new file mode 100644 index 0000000000000..3b8f02a749495 --- /dev/null +++ b/tests/cases/compiler/jsonLiteralTypesDefault.ts @@ -0,0 +1,32 @@ +// @resolveJsonModule: true +// @esModuleInterop: true +// @module: commonjs +// @target: esnext +// @strict: true + +// @Filename: data.json +{ + "s": "string literal", + "n": 123, + "b": true, + "arr": ["a", "b"] +} + +// @Filename: main.ts +import data from "./data.json"; + +// Should be wide types +const s: string = data.s; +const n: number = data.n; +const b: boolean = data.b; +const arr: string[] = data.arr; + +// Should NOT be literal types (these assignments should fail if they were literals, but since we are assigning TO them, we check what they ARE) +// Actually, data.s is string. So `const x: "literal" = data.s` would fail if data.s is string. +// But here data.s IS string. +// Let's verify by assigning to literals which should fail if it is string. + +const literalS: "string literal" = data.s; // Error expected: string not assignable to "string literal" +const literalN: 123 = data.n; // Error expected +const literalB: true = data.b; // Error expected +const literalArr: readonly ["a", "b"] = data.arr; // Error expected From 0d82d8ddf42f95afb845bcb774737f8c136167e0 Mon Sep 17 00:00:00 2001 From: James Prevett Date: Sat, 17 Jan 2026 21:49:37 -0600 Subject: [PATCH 3/3] Added typescript.d.ts baseline --- tests/baselines/reference/api/typescript.d.ts | 1 + .../importJsonAsConst/tsconfig.json | 5 +++++ tests/baselines/reference/tsc/commandLine/help-all.js | 5 +++++ 3 files changed, 11 insertions(+) create mode 100644 tests/baselines/reference/config/showConfig/Shows tsconfig for single option/importJsonAsConst/tsconfig.json diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 6ca73b0b2fe8b..3c4c62761e654 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -7039,6 +7039,7 @@ declare namespace ts { forceConsistentCasingInFileNames?: boolean; ignoreDeprecations?: string; importHelpers?: boolean; + importJsonAsConst?: boolean; /** @deprecated */ importsNotUsedAsValues?: ImportsNotUsedAsValues; inlineSourceMap?: boolean; diff --git a/tests/baselines/reference/config/showConfig/Shows tsconfig for single option/importJsonAsConst/tsconfig.json b/tests/baselines/reference/config/showConfig/Shows tsconfig for single option/importJsonAsConst/tsconfig.json new file mode 100644 index 0000000000000..6f8c5571da393 --- /dev/null +++ b/tests/baselines/reference/config/showConfig/Shows tsconfig for single option/importJsonAsConst/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "importJsonAsConst": true + } +} diff --git a/tests/baselines/reference/tsc/commandLine/help-all.js b/tests/baselines/reference/tsc/commandLine/help-all.js index f0a4d50e418a5..2484b4b63da7c 100644 --- a/tests/baselines/reference/tsc/commandLine/help-all.js +++ b/tests/baselines/reference/tsc/commandLine/help-all.js @@ -555,6 +555,11 @@ Enable experimental support for legacy experimental decorators. type: boolean default: false +--importJsonAsConst +Import JSON files as const assertions. +type: boolean +default: false + --jsx Specify what JSX code is generated. one of: preserve, react, react-native, react-jsx, react-jsxdev