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 internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -13216,6 +13216,9 @@ func (c *Checker) hasDefaultValue(node *ast.Node) bool {

func (c *Checker) isConstContext(node *ast.Node) bool {
parent := node.Parent
if c.compilerOptions.ImportJsonAsConst == core.TSTrue && parent.Kind == ast.KindExpressionStatement && ast.IsSourceFile(parent.Parent) && ast.IsJsonSourceFile(parent.Parent.AsSourceFile()) {
return true
}
return ast.IsConstAssertion(parent) ||
c.isValidConstAssertionArgument(node) && c.isConstTypeVariable(c.getContextualType(node, ContextFlagsNone), 0) ||
(ast.IsParenthesizedExpression(parent) || ast.IsArrayLiteralExpression(parent) || ast.IsSpreadElement(parent)) && c.isConstContext(parent) ||
Expand Down Expand Up @@ -16153,6 +16156,9 @@ func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *ast.Symbo
if len(statements) == 0 {
return c.emptyObjectType
}
if c.compilerOptions.ImportJsonAsConst == core.TSTrue {
return c.checkExpression(statements[0].Expression())
}
return c.getWidenedType(c.getWidenedLiteralType(c.checkExpression(statements[0].Expression())))
}
// Handle variable, parameter or property
Expand Down
1 change: 1 addition & 0 deletions internal/core/compileroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type CompilerOptions struct {
IgnoreConfig Tristate `json:"ignoreConfig,omitzero"`
IgnoreDeprecations string `json:"ignoreDeprecations,omitzero"`
ImportHelpers Tristate `json:"importHelpers,omitzero"`
ImportJsonAsConst Tristate `json:"importJsonAsConst,omitzero"`
InlineSourceMap Tristate `json:"inlineSourceMap,omitzero"`
InlineSources Tristate `json:"inlineSources,omitzero"`
Init Tristate `json:"init,omitzero"`
Expand Down
4 changes: 4 additions & 0 deletions internal/diagnostics/diagnostics_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions internal/diagnostics/extraDiagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,9 @@
"Deduplicate packages with the same name and version.": {
"category": "Message",
"code": 100011
},
"Import JSON files as const assertions.": {
"category": "Message",
"code": 6933
}
}
9 changes: 9 additions & 0 deletions internal/tsoptions/declscompiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,15 @@ var optionsForCompiler = []*CommandLineOption{
Description: diagnostics.Allow_importing_helper_functions_from_tslib_once_per_project_instead_of_including_them_per_file,
DefaultValueDescription: false,
},
{
Name: "importJsonAsConst",
Kind: CommandLineOptionTypeBoolean,
AffectsSemanticDiagnostics: true,
AffectsBuildInfo: true,
Category: diagnostics.Language_and_Environment,
Description: diagnostics.Import_JSON_files_as_const_assertions,
DefaultValueDescription: false,
},
{
Name: "downlevelIteration",
Kind: CommandLineOptionTypeBoolean,
Expand Down
2 changes: 2 additions & 0 deletions internal/tsoptions/parsinghelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ func parseCompilerOptions(key string, value any, allOptions *core.CompilerOption
allOptions.IgnoreDeprecations = ParseString(value)
case "importHelpers":
allOptions.ImportHelpers = ParseTristate(value)
case "importJsonAsConst":
allOptions.ImportJsonAsConst = ParseTristate(value)
case "incremental":
allOptions.Incremental = ParseTristate(value)
case "init":
Expand Down
30 changes: 30 additions & 0 deletions testdata/baselines/reference/compiler/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 testdata/baselines/reference/compiler/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))

54 changes: 54 additions & 0 deletions testdata/baselines/reference/compiler/jsonLiteralTypes.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//// [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"]

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 testdata/baselines/reference/compiler/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
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