Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) ||
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7442,6 +7442,7 @@ export interface CompilerOptions {
/** @internal */ help?: boolean;
ignoreDeprecations?: string;
importHelpers?: boolean;
importJsonAsConst?: boolean;
/** @deprecated */
importsNotUsedAsValues?: ImportsNotUsedAsValues;
/** @internal */ init?: boolean;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7039,6 +7039,7 @@ declare namespace ts {
forceConsistentCasingInFileNames?: boolean;
ignoreDeprecations?: string;
importHelpers?: boolean;
importJsonAsConst?: boolean;
/** @deprecated */
importsNotUsedAsValues?: ImportsNotUsedAsValues;
inlineSourceMap?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"importJsonAsConst": true
}
}
30 changes: 30 additions & 0 deletions tests/baselines/reference/jsonLiteralTypes.js
Original file line number Diff line number Diff line change
@@ -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;
45 changes: 45 additions & 0 deletions tests/baselines/reference/jsonLiteralTypes.symbols
Original file line number Diff line number Diff line change
@@ -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))

83 changes: 83 additions & 0 deletions tests/baselines/reference/jsonLiteralTypes.types
Original file line number Diff line number Diff line change
@@ -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"]
> : ^^^^^^^^^^^^^^^^^^^

43 changes: 43 additions & 0 deletions tests/baselines/reference/jsonLiteralTypesDefault.errors.txt
Original file line number Diff line number Diff line change
@@ -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.

50 changes: 50 additions & 0 deletions tests/baselines/reference/jsonLiteralTypesDefault.js
Original file line number Diff line number Diff line change
@@ -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
Loading