Skip to content

Commit aa39080

Browse files
authored
Do not allow freshness to move errors out of the current file, ensure json documents are deeply unfreshened and fully widened (#35048)
1 parent f334476 commit aa39080

6 files changed

+158
-8
lines changed

src/compiler/checker.ts

+3-7
Original file line numberDiff line numberDiff line change
@@ -7310,11 +7310,7 @@ namespace ts {
73107310
if (!declaration.statements.length) {
73117311
return emptyObjectType;
73127312
}
7313-
const type = getWidenedLiteralType(checkExpression(declaration.statements[0].expression));
7314-
if (type.flags & TypeFlags.Object) {
7315-
return getRegularTypeOfObjectLiteral(type);
7316-
}
7317-
return type;
7313+
return getWidenedType(getWidenedLiteralType(checkExpression(declaration.statements[0].expression)));
73187314
}
73197315

73207316
// Handle variable, parameter or property
@@ -14807,7 +14803,7 @@ namespace ts {
1480714803
// JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal.
1480814804
// However, using an object-literal error message will be very confusing to the users so we give different a message.
1480914805
// TODO: Spelling suggestions for excess jsx attributes (needs new diagnostic messages)
14810-
if (prop.valueDeclaration && isJsxAttribute(prop.valueDeclaration)) {
14806+
if (prop.valueDeclaration && isJsxAttribute(prop.valueDeclaration) && getSourceFileOfNode(errorNode) === getSourceFileOfNode(prop.valueDeclaration.name)) {
1481114807
// Note that extraneous children (as in `<NoChild>extra</NoChild>`) don't pass this check,
1481214808
// since `children` is a SyntaxKind.PropertySignature instead of a SyntaxKind.JsxAttribute.
1481314809
errorNode = prop.valueDeclaration.name;
@@ -14818,7 +14814,7 @@ namespace ts {
1481814814
// use the property's value declaration if the property is assigned inside the literal itself
1481914815
const objectLiteralDeclaration = source.symbol && firstOrUndefined(source.symbol.declarations);
1482014816
let suggestion;
14821-
if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration)) {
14817+
if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration) && getSourceFileOfNode(objectLiteralDeclaration) === getSourceFileOfNode(errorNode)) {
1482214818
const propDeclaration = prop.valueDeclaration as ObjectLiteralElementLike;
1482314819
Debug.assertNode(propDeclaration, isObjectLiteralElementLike);
1482414820

tests/baselines/reference/jsDeclarationsJson.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
=== tests/cases/conformance/jsdoc/declarations/index.js ===
22
const j = require("./obj.json");
33
>j : { x: number; y: number; obj: { items: ({ x: number; y?: undefined; err?: undefined; } | { x: number; y: number; err?: undefined; } | { x: number; err: boolean; y?: undefined; })[]; }; }
4-
>require("./obj.json") : { x: number; y: number; obj: { items: ({ x: number; } | { x: number; y: number; } | { x: number; err: boolean; })[]; }; }
4+
>require("./obj.json") : { x: number; y: number; obj: { items: ({ x: number; y?: undefined; err?: undefined; } | { x: number; y: number; err?: undefined; } | { x: number; err: boolean; y?: undefined; })[]; }; }
55
>require : any
66
>"./obj.json" : "./obj.json"
77

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [tests/cases/compiler/jsonFileImportChecksCallCorrectlyTwice.ts] ////
2+
3+
//// [index.ts]
4+
import data from "./data.json";
5+
6+
interface Foo {
7+
str: string;
8+
}
9+
10+
fn(data.foo);
11+
fn(data.foo); // <-- shouldn't error!
12+
13+
function fn(arg: Foo[]) { }
14+
//// [data.json]
15+
{
16+
"foo": [
17+
{
18+
"bool": true,
19+
"str": "123"
20+
}
21+
]
22+
}
23+
24+
//// [data.json]
25+
{
26+
"foo": [
27+
{
28+
"bool": true,
29+
"str": "123"
30+
}
31+
]
32+
}
33+
//// [index.js]
34+
"use strict";
35+
var __importDefault = (this && this.__importDefault) || function (mod) {
36+
return (mod && mod.__esModule) ? mod : { "default": mod };
37+
};
38+
exports.__esModule = true;
39+
var data_json_1 = __importDefault(require("./data.json"));
40+
fn(data_json_1["default"].foo);
41+
fn(data_json_1["default"].foo); // <-- shouldn't error!
42+
function fn(arg) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
=== tests/cases/compiler/index.ts ===
2+
import data from "./data.json";
3+
>data : Symbol(data, Decl(index.ts, 0, 6))
4+
5+
interface Foo {
6+
>Foo : Symbol(Foo, Decl(index.ts, 0, 31))
7+
8+
str: string;
9+
>str : Symbol(Foo.str, Decl(index.ts, 2, 15))
10+
}
11+
12+
fn(data.foo);
13+
>fn : Symbol(fn, Decl(index.ts, 7, 13))
14+
>data.foo : Symbol("foo", Decl(data.json, 0, 1))
15+
>data : Symbol(data, Decl(index.ts, 0, 6))
16+
>foo : Symbol("foo", Decl(data.json, 0, 1))
17+
18+
fn(data.foo); // <-- shouldn't error!
19+
>fn : Symbol(fn, Decl(index.ts, 7, 13))
20+
>data.foo : Symbol("foo", Decl(data.json, 0, 1))
21+
>data : Symbol(data, Decl(index.ts, 0, 6))
22+
>foo : Symbol("foo", Decl(data.json, 0, 1))
23+
24+
function fn(arg: Foo[]) { }
25+
>fn : Symbol(fn, Decl(index.ts, 7, 13))
26+
>arg : Symbol(arg, Decl(index.ts, 9, 12))
27+
>Foo : Symbol(Foo, Decl(index.ts, 0, 31))
28+
29+
=== tests/cases/compiler/data.json ===
30+
{
31+
"foo": [
32+
>"foo" : Symbol("foo", Decl(data.json, 0, 1))
33+
{
34+
"bool": true,
35+
>"bool" : Symbol("bool", Decl(data.json, 2, 7))
36+
37+
"str": "123"
38+
>"str" : Symbol("str", Decl(data.json, 3, 21))
39+
}
40+
]
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/compiler/index.ts ===
2+
import data from "./data.json";
3+
>data : { foo: { bool: boolean; str: string; }[]; }
4+
5+
interface Foo {
6+
str: string;
7+
>str : string
8+
}
9+
10+
fn(data.foo);
11+
>fn(data.foo) : void
12+
>fn : (arg: Foo[]) => void
13+
>data.foo : { bool: boolean; str: string; }[]
14+
>data : { foo: { bool: boolean; str: string; }[]; }
15+
>foo : { bool: boolean; str: string; }[]
16+
17+
fn(data.foo); // <-- shouldn't error!
18+
>fn(data.foo) : void
19+
>fn : (arg: Foo[]) => void
20+
>data.foo : { bool: boolean; str: string; }[]
21+
>data : { foo: { bool: boolean; str: string; }[]; }
22+
>foo : { bool: boolean; str: string; }[]
23+
24+
function fn(arg: Foo[]) { }
25+
>fn : (arg: Foo[]) => void
26+
>arg : Foo[]
27+
28+
=== tests/cases/compiler/data.json ===
29+
{
30+
>{ "foo": [ { "bool": true, "str": "123" } ]} : { foo: { bool: boolean; str: string; }[]; }
31+
32+
"foo": [
33+
>"foo" : { bool: boolean; str: string; }[]
34+
>[ { "bool": true, "str": "123" } ] : { bool: boolean; str: string; }[]
35+
{
36+
>{ "bool": true, "str": "123" } : { bool: boolean; str: string; }
37+
38+
"bool": true,
39+
>"bool" : boolean
40+
>true : true
41+
42+
"str": "123"
43+
>"str" : string
44+
>"123" : "123"
45+
}
46+
]
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @esModuleInterop: true
2+
// @resolveJsonModule: true
3+
// @strict: true
4+
// @outDir: dist
5+
// @filename: index.ts
6+
import data from "./data.json";
7+
8+
interface Foo {
9+
str: string;
10+
}
11+
12+
fn(data.foo);
13+
fn(data.foo); // <-- shouldn't error!
14+
15+
function fn(arg: Foo[]) { }
16+
// @filename: data.json
17+
{
18+
"foo": [
19+
{
20+
"bool": true,
21+
"str": "123"
22+
}
23+
]
24+
}

0 commit comments

Comments
 (0)