Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
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
75 changes: 75 additions & 0 deletions tests/baselines/reference/jsonLiteralTypesDefault.symbols
Original file line number Diff line number Diff line change
@@ -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))

Loading
Loading