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
1 change: 1 addition & 0 deletions packages/typescript/src/api/proto.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1431,6 +1431,7 @@ export interface CompilerOptions {
ignoreConfig?: boolean | undefined;
ignoreDeprecations?: string | undefined;
importHelpers?: boolean | undefined;
importJsonAsConst?: boolean | undefined;
inlineSourceMap?: boolean | undefined;
inlineSources?: boolean | undefined;
init?: boolean | undefined;
Expand Down
6 changes: 6 additions & 0 deletions tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -13815,6 +13815,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.isInlineImportAttributes(node) ||
c.isValidConstAssertionArgument(node) && c.isConstTypeVariable(c.getContextualType(node, ContextFlagsNone), 0) ||
Expand Down Expand Up @@ -16893,6 +16896,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 tsc/internal/core/compileroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,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 tsc/internal/diagnostics/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -6751,6 +6751,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
4 changes: 4 additions & 0 deletions tsc/internal/diagnostics/diagnostics_generated.go

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

2 changes: 1 addition & 1 deletion tsc/internal/execute/tsc/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func getOptionsForHelp(commandLine *tsoptions.ParsedCommandLine) []*tsoptions.Co
opts = append(opts, &tsoptions.TscBuildOption)

if commandLine.CompilerOptions().All.IsTrue() {
slices.SortFunc(opts, func(a, b *tsoptions.CommandLineOption) int {
slices.SortStableFunc(opts, func(a, b *tsoptions.CommandLineOption) int {
return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name))
})
return opts
Expand Down
9 changes: 9 additions & 0 deletions tsc/internal/tsoptions/declscompiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,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 tsc/internal/tsoptions/parsinghelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,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 tsc/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 tsc/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 tsc/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,39 @@
main.ts(10,7): error TS2322: Type 'string' is not assignable to type '"string literal"'.
main.ts(11,7): error TS2322: Type 'number' is not assignable to type '123'.
main.ts(12,7): error TS2322: Type 'boolean' is not assignable to type 'true'.
main.ts(13,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";

// Without importJsonAsConst, JSON properties have widened types.
const s: string = data.s;
const n: number = data.n;
const b: boolean = data.b;
const arr: string[] = data.arr;

// Assigning them to literal types therefore errors.
const literalS: "string literal" = data.s;
~~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type '"string literal"'.
const literalN: 123 = data.n;
~~~~~~~~
!!! error TS2322: Type 'number' is not assignable to type '123'.
const literalB: true = data.b;
~~~~~~~~
!!! error TS2322: Type 'boolean' is not assignable to type 'true'.
const literalArr: readonly ["a", "b"] = data.arr;
~~~~~~~~~~
!!! error TS2322: Type 'string[]' is not assignable to type 'readonly ["a", "b"]'.
!!! error TS2322: Target requires 2 element(s) but source may have fewer.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//// [tests/cases/compiler/jsonLiteralTypesDefault.ts] ////

//// [data.json]
{
"s": "string literal",
"n": 123,
"b": true,
"arr": ["a", "b"]
}

//// [main.ts]
import data from "./data.json";

// Without importJsonAsConst, JSON properties have widened types.
const s: string = data.s;
const n: number = data.n;
const b: boolean = data.b;
const arr: string[] = data.arr;

// Assigning them to literal types therefore errors.
const literalS: "string literal" = data.s;
const literalN: 123 = data.n;
const literalB: true = data.b;
const literalArr: 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"));
// Without importJsonAsConst, JSON properties have widened 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;
// Assigning them to literal types therefore errors.
const literalS = data_json_1.default.s;
const literalN = data_json_1.default.n;
const literalB = data_json_1.default.b;
const literalArr = data_json_1.default.arr;
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//// [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))

// Without importJsonAsConst, JSON properties have widened 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))

// Assigning them to literal types therefore errors.
const literalS: "string literal" = data.s;
>literalS : Symbol(literalS, Decl(main.ts, 9, 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;
>literalN : Symbol(literalN, Decl(main.ts, 10, 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;
>literalB : Symbol(literalB, Decl(main.ts, 11, 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;
>literalArr : Symbol(literalArr, Decl(main.ts, 12, 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