Skip to content

Commit 86d79aa

Browse files
committed
fix jsx error location when empty JsxExpression precedes failing child
1 parent 7539c04 commit 86d79aa

6 files changed

Lines changed: 313 additions & 0 deletions

src/compiler/checker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21702,6 +21702,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2170221702
switch (child.kind) {
2170321703
case SyntaxKind.JsxExpression:
2170421704
// child is of the type of the expression
21705+
// empty JsxExpression ({/* comment */}) has no expression — skip it the same way
21706+
// getSemanticJsxChildren and checkJsxChildren do, so nameType indices stay aligned
21707+
if (!child.expression) {
21708+
break;
21709+
}
2170521710
return { errorNode: child, innerExpression: child.expression, nameType };
2170621711
case SyntaxKind.JsxText:
2170721712
if (child.containsOnlyTriviaWhiteSpaces) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
index.tsx(17,5): error TS2322: Type '{ notAString: true; }' is not assignable to type 'string'.
2+
index.tsx(18,5): error TS2322: Type 'Element' is not assignable to type 'string'.
3+
index.tsx(23,5): error TS2322: Type '{ notAString: true; }' is not assignable to type 'string'.
4+
index.tsx(24,5): error TS2322: Type 'Element' is not assignable to type 'string'.
5+
index.tsx(28,10): error TS2745: This JSX tag's 'children' prop expects type 'string[]' which requires multiple children, but only a single child was provided.
6+
7+
8+
==== index.tsx (5 errors) ====
9+
/// <reference path="/.lib/react16.d.ts" />
10+
import * as React from "react";
11+
12+
interface Props {
13+
children: string[];
14+
}
15+
16+
export function Comp(props: Props) {
17+
return <></>;
18+
}
19+
20+
declare const badValue: { notAString: true };
21+
22+
// Error should be on {badValue}, not on {/* */}
23+
var a = <Comp>
24+
{/* */}
25+
{badValue}
26+
~~~~~~~~~~
27+
!!! error TS2322: Type '{ notAString: true; }' is not assignable to type 'string'.
28+
<br />
29+
~~~~~~
30+
!!! error TS2322: Type 'Element' is not assignable to type 'string'.
31+
</Comp>
32+
33+
// No comment before — error should also be on {badValue}
34+
var b = <Comp>
35+
{badValue}
36+
~~~~~~~~~~
37+
!!! error TS2322: Type '{ notAString: true; }' is not assignable to type 'string'.
38+
<br />
39+
~~~~~~
40+
!!! error TS2322: Type 'Element' is not assignable to type 'string'.
41+
</Comp>
42+
43+
// Comment after — should not affect error location
44+
var c = <Comp>
45+
~~~~
46+
!!! error TS2745: This JSX tag's 'children' prop expects type 'string[]' which requires multiple children, but only a single child was provided.
47+
{badValue}
48+
{/* */}
49+
</Comp>
50+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//// [tests/cases/compiler/jsxCommentExpressionDoesNotStealErrorLocation.tsx] ////
2+
3+
//// [index.tsx]
4+
/// <reference path="/.lib/react16.d.ts" />
5+
import * as React from "react";
6+
7+
interface Props {
8+
children: string[];
9+
}
10+
11+
export function Comp(props: Props) {
12+
return <></>;
13+
}
14+
15+
declare const badValue: { notAString: true };
16+
17+
// Error should be on {badValue}, not on {/* */}
18+
var a = <Comp>
19+
{/* */}
20+
{badValue}
21+
<br />
22+
</Comp>
23+
24+
// No comment before — error should also be on {badValue}
25+
var b = <Comp>
26+
{badValue}
27+
<br />
28+
</Comp>
29+
30+
// Comment after — should not affect error location
31+
var c = <Comp>
32+
{badValue}
33+
{/* */}
34+
</Comp>
35+
36+
37+
//// [index.js]
38+
/// <reference path="/.lib/react16.d.ts" />
39+
import * as React from "react";
40+
export function Comp(props) {
41+
return React.createElement(React.Fragment, null);
42+
}
43+
// Error should be on {badValue}, not on {/* */}
44+
var a = React.createElement(Comp, null,
45+
badValue,
46+
React.createElement("br", null));
47+
// No comment before — error should also be on {badValue}
48+
var b = React.createElement(Comp, null,
49+
badValue,
50+
React.createElement("br", null));
51+
// Comment after — should not affect error location
52+
var c = React.createElement(Comp, null, badValue);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//// [tests/cases/compiler/jsxCommentExpressionDoesNotStealErrorLocation.tsx] ////
2+
3+
=== index.tsx ===
4+
/// <reference path="react16.d.ts" />
5+
import * as React from "react";
6+
>React : Symbol(React, Decl(index.tsx, 1, 6))
7+
8+
interface Props {
9+
>Props : Symbol(Props, Decl(index.tsx, 1, 31))
10+
11+
children: string[];
12+
>children : Symbol(Props.children, Decl(index.tsx, 3, 17))
13+
}
14+
15+
export function Comp(props: Props) {
16+
>Comp : Symbol(Comp, Decl(index.tsx, 5, 1))
17+
>props : Symbol(props, Decl(index.tsx, 7, 21))
18+
>Props : Symbol(Props, Decl(index.tsx, 1, 31))
19+
20+
return <></>;
21+
}
22+
23+
declare const badValue: { notAString: true };
24+
>badValue : Symbol(badValue, Decl(index.tsx, 11, 13))
25+
>notAString : Symbol(notAString, Decl(index.tsx, 11, 25))
26+
27+
// Error should be on {badValue}, not on {/* */}
28+
var a = <Comp>
29+
>a : Symbol(a, Decl(index.tsx, 14, 3))
30+
>Comp : Symbol(Comp, Decl(index.tsx, 5, 1))
31+
32+
{/* */}
33+
{badValue}
34+
>badValue : Symbol(badValue, Decl(index.tsx, 11, 13))
35+
36+
<br />
37+
>br : Symbol(JSX.IntrinsicElements.br, Decl(react16.d.ts, 2533, 102))
38+
39+
</Comp>
40+
>Comp : Symbol(Comp, Decl(index.tsx, 5, 1))
41+
42+
// No comment before — error should also be on {badValue}
43+
var b = <Comp>
44+
>b : Symbol(b, Decl(index.tsx, 21, 3))
45+
>Comp : Symbol(Comp, Decl(index.tsx, 5, 1))
46+
47+
{badValue}
48+
>badValue : Symbol(badValue, Decl(index.tsx, 11, 13))
49+
50+
<br />
51+
>br : Symbol(JSX.IntrinsicElements.br, Decl(react16.d.ts, 2533, 102))
52+
53+
</Comp>
54+
>Comp : Symbol(Comp, Decl(index.tsx, 5, 1))
55+
56+
// Comment after — should not affect error location
57+
var c = <Comp>
58+
>c : Symbol(c, Decl(index.tsx, 27, 3))
59+
>Comp : Symbol(Comp, Decl(index.tsx, 5, 1))
60+
61+
{badValue}
62+
>badValue : Symbol(badValue, Decl(index.tsx, 11, 13))
63+
64+
{/* */}
65+
</Comp>
66+
>Comp : Symbol(Comp, Decl(index.tsx, 5, 1))
67+
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//// [tests/cases/compiler/jsxCommentExpressionDoesNotStealErrorLocation.tsx] ////
2+
3+
=== Performance Stats ===
4+
Assignability cache: 2,500
5+
Type Count: 10,000
6+
Instantiation count: 100,000
7+
Symbol count: 50,000
8+
9+
=== index.tsx ===
10+
/// <reference path="react16.d.ts" />
11+
import * as React from "react";
12+
>React : typeof React
13+
> : ^^^^^^^^^^^^
14+
15+
interface Props {
16+
children: string[];
17+
>children : string[]
18+
> : ^^^^^^^^
19+
}
20+
21+
export function Comp(props: Props) {
22+
>Comp : (props: Props) => JSX.Element
23+
> : ^ ^^ ^^^^^^^^^^^^^^^^
24+
>props : Props
25+
> : ^^^^^
26+
27+
return <></>;
28+
><></> : JSX.Element
29+
> : ^^^^^^^^^^^
30+
}
31+
32+
declare const badValue: { notAString: true };
33+
>badValue : { notAString: true; }
34+
> : ^^^^^^^^^^^^^^ ^^^
35+
>notAString : true
36+
> : ^^^^
37+
>true : true
38+
> : ^^^^
39+
40+
// Error should be on {badValue}, not on {/* */}
41+
var a = <Comp>
42+
>a : JSX.Element
43+
> : ^^^^^^^^^^^
44+
><Comp> {/* */} {badValue} <br /></Comp> : JSX.Element
45+
> : ^^^^^^^^^^^
46+
>Comp : (props: Props) => JSX.Element
47+
> : ^ ^^ ^^^^^^^^^^^^^^^^
48+
49+
{/* */}
50+
{badValue}
51+
>badValue : { notAString: true; }
52+
> : ^^^^^^^^^^^^^^ ^^^
53+
54+
<br />
55+
><br /> : JSX.Element
56+
> : ^^^^^^^^^^^
57+
>br : any
58+
> : ^^^
59+
60+
</Comp>
61+
>Comp : (props: Props) => JSX.Element
62+
> : ^ ^^ ^^^^^^^^^^^^^^^^
63+
64+
// No comment before — error should also be on {badValue}
65+
var b = <Comp>
66+
>b : JSX.Element
67+
> : ^^^^^^^^^^^
68+
><Comp> {badValue} <br /></Comp> : JSX.Element
69+
> : ^^^^^^^^^^^
70+
>Comp : (props: Props) => JSX.Element
71+
> : ^ ^^ ^^^^^^^^^^^^^^^^
72+
73+
{badValue}
74+
>badValue : { notAString: true; }
75+
> : ^^^^^^^^^^^^^^ ^^^
76+
77+
<br />
78+
><br /> : JSX.Element
79+
> : ^^^^^^^^^^^
80+
>br : any
81+
> : ^^^
82+
83+
</Comp>
84+
>Comp : (props: Props) => JSX.Element
85+
> : ^ ^^ ^^^^^^^^^^^^^^^^
86+
87+
// Comment after — should not affect error location
88+
var c = <Comp>
89+
>c : JSX.Element
90+
> : ^^^^^^^^^^^
91+
><Comp> {badValue} {/* */}</Comp> : JSX.Element
92+
> : ^^^^^^^^^^^
93+
>Comp : (props: Props) => JSX.Element
94+
> : ^ ^^ ^^^^^^^^^^^^^^^^
95+
96+
{badValue}
97+
>badValue : { notAString: true; }
98+
> : ^^^^^^^^^^^^^^ ^^^
99+
100+
{/* */}
101+
</Comp>
102+
>Comp : (props: Props) => JSX.Element
103+
> : ^ ^^ ^^^^^^^^^^^^^^^^
104+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// @target: es2015
2+
// @jsx: react
3+
// @strict: true
4+
// @filename: index.tsx
5+
/// <reference path="/.lib/react16.d.ts" />
6+
import * as React from "react";
7+
8+
interface Props {
9+
children: string[];
10+
}
11+
12+
export function Comp(props: Props) {
13+
return <></>;
14+
}
15+
16+
declare const badValue: { notAString: true };
17+
18+
// Error should be on {badValue}, not on {/* */}
19+
var a = <Comp>
20+
{/* */}
21+
{badValue}
22+
<br />
23+
</Comp>
24+
25+
// No comment before — error should also be on {badValue}
26+
var b = <Comp>
27+
{badValue}
28+
<br />
29+
</Comp>
30+
31+
// Comment after — should not affect error location
32+
var c = <Comp>
33+
{badValue}
34+
{/* */}
35+
</Comp>

0 commit comments

Comments
 (0)