Skip to content

Commit 6b90119

Browse files
Merge pull request #19968 from uniqueiniquity/jsxFragmentFix
Check children of JSXFragment
2 parents c2f0c58 + 4c26426 commit 6b90119

File tree

6 files changed

+253
-14
lines changed

6 files changed

+253
-14
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14378,19 +14378,7 @@ namespace ts {
1437814378
const parent = openingLikeElement.parent.kind === SyntaxKind.JsxElement ? openingLikeElement.parent as JsxElement : undefined;
1437914379
// We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement
1438014380
if (parent && parent.openingElement === openingLikeElement && parent.children.length > 0) {
14381-
const childrenTypes: Type[] = [];
14382-
for (const child of (parent as JsxElement).children) {
14383-
// In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that
14384-
// because then type of children property will have constituent of string type.
14385-
if (child.kind === SyntaxKind.JsxText) {
14386-
if (!child.containsOnlyWhiteSpaces) {
14387-
childrenTypes.push(stringType);
14388-
}
14389-
}
14390-
else {
14391-
childrenTypes.push(checkExpression(child, checkMode));
14392-
}
14393-
}
14381+
const childrenTypes: Type[] = checkJsxChildren(parent as JsxElement, checkMode);
1439414382

1439514383
if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") {
1439614384
// Error if there is a attribute named "children" explicitly specified and children element.
@@ -14430,6 +14418,23 @@ namespace ts {
1443014418
}
1443114419
}
1443214420

14421+
function checkJsxChildren(node: JsxElement | JsxFragment, checkMode?: CheckMode) {
14422+
const childrenTypes: Type[] = [];
14423+
for (const child of node.children) {
14424+
// In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that
14425+
// because then type of children property will have constituent of string type.
14426+
if (child.kind === SyntaxKind.JsxText) {
14427+
if (!child.containsOnlyWhiteSpaces) {
14428+
childrenTypes.push(stringType);
14429+
}
14430+
}
14431+
else {
14432+
childrenTypes.push(checkExpression(child, checkMode));
14433+
}
14434+
}
14435+
return childrenTypes;
14436+
}
14437+
1443314438
/**
1443414439
* Check attributes property of opening-like element. This function is called during chooseOverload to get call signature of a JSX opening-like element.
1443514440
* (See "checkApplicableSignatureForJsxOpeningLikeElement" for how the function is used)
@@ -14964,6 +14969,9 @@ namespace ts {
1496414969
if (isNodeOpeningLikeElement) {
1496514970
checkJsxAttributesAssignableToTagNameAttributes(<JsxOpeningLikeElement>node);
1496614971
}
14972+
else {
14973+
checkJsxChildren((node as JsxOpeningFragment).parent);
14974+
}
1496714975
}
1496814976

1496914977
/**
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
tests/cases/compiler/jsxFactoryAndFragment.tsx(3,1): error TS17016: JSX fragment is not supported when using --jsxFactory
22
tests/cases/compiler/jsxFactoryAndFragment.tsx(4,1): error TS17016: JSX fragment is not supported when using --jsxFactory
3+
tests/cases/compiler/jsxFactoryAndFragment.tsx(4,17): error TS17016: JSX fragment is not supported when using --jsxFactory
34

45

5-
==== tests/cases/compiler/jsxFactoryAndFragment.tsx (2 errors) ====
6+
==== tests/cases/compiler/jsxFactoryAndFragment.tsx (3 errors) ====
67
declare var h: any;
78

89
<></>;
910
~~~~~
1011
!!! error TS17016: JSX fragment is not supported when using --jsxFactory
1112
<><span>1</span><><span>2.1</span><span>2.2</span></></>;
1213
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14+
!!! error TS17016: JSX fragment is not supported when using --jsxFactory
15+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1316
!!! error TS17016: JSX fragment is not supported when using --jsxFactory
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//// [tests/cases/compiler/tsxFragmentChildrenCheck.ts] ////
2+
3+
//// [my-component.tsx]
4+
declare var React: any;
5+
6+
export function MyComponent(props: any) {
7+
return <span>my component</span>;
8+
}
9+
10+
//// [file1.tsx]
11+
import * as React from 'react'
12+
import { MyComponent } from './my-component'
13+
14+
const MY_STRING: string = 'Ceci n\'est pas une string.'
15+
const MY_CLASSNAME: string = 'jeclass'
16+
17+
class RenderString extends React.PureComponent<any, any> {
18+
render() {
19+
return (
20+
<>
21+
<MyComponent />
22+
<span>{ MY_STRING }</span>
23+
<span className={ MY_CLASSNAME } />
24+
</>
25+
)
26+
}
27+
}
28+
29+
export default RenderString
30+
31+
//// [my-component.js]
32+
"use strict";
33+
exports.__esModule = true;
34+
function MyComponent(props) {
35+
return React.createElement("span", null, "my component");
36+
}
37+
exports.MyComponent = MyComponent;
38+
//// [file1.js]
39+
"use strict";
40+
var __extends = (this && this.__extends) || (function () {
41+
var extendStatics = Object.setPrototypeOf ||
42+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
43+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
44+
return function (d, b) {
45+
extendStatics(d, b);
46+
function __() { this.constructor = d; }
47+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
48+
};
49+
})();
50+
exports.__esModule = true;
51+
var React = require("react");
52+
var my_component_1 = require("./my-component");
53+
var MY_STRING = 'Ceci n\'est pas une string.';
54+
var MY_CLASSNAME = 'jeclass';
55+
var RenderString = /** @class */ (function (_super) {
56+
__extends(RenderString, _super);
57+
function RenderString() {
58+
return _super !== null && _super.apply(this, arguments) || this;
59+
}
60+
RenderString.prototype.render = function () {
61+
return (React.createElement(React.Fragment, null,
62+
React.createElement(my_component_1.MyComponent, null),
63+
React.createElement("span", null, MY_STRING),
64+
React.createElement("span", { className: MY_CLASSNAME })));
65+
};
66+
return RenderString;
67+
}(React.PureComponent));
68+
exports["default"] = RenderString;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
=== tests/cases/compiler/my-component.tsx ===
2+
declare var React: any;
3+
>React : Symbol(React, Decl(my-component.tsx, 0, 11))
4+
5+
export function MyComponent(props: any) {
6+
>MyComponent : Symbol(MyComponent, Decl(my-component.tsx, 0, 23))
7+
>props : Symbol(props, Decl(my-component.tsx, 2, 28))
8+
9+
return <span>my component</span>;
10+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2461, 51))
11+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2461, 51))
12+
}
13+
14+
=== tests/cases/compiler/file1.tsx ===
15+
import * as React from 'react'
16+
>React : Symbol(React, Decl(file1.tsx, 0, 6))
17+
18+
import { MyComponent } from './my-component'
19+
>MyComponent : Symbol(MyComponent, Decl(file1.tsx, 1, 8))
20+
21+
const MY_STRING: string = 'Ceci n\'est pas une string.'
22+
>MY_STRING : Symbol(MY_STRING, Decl(file1.tsx, 3, 5))
23+
24+
const MY_CLASSNAME: string = 'jeclass'
25+
>MY_CLASSNAME : Symbol(MY_CLASSNAME, Decl(file1.tsx, 4, 5))
26+
27+
class RenderString extends React.PureComponent<any, any> {
28+
>RenderString : Symbol(RenderString, Decl(file1.tsx, 4, 38))
29+
>React.PureComponent : Symbol(React.PureComponent, Decl(react.d.ts, 180, 5))
30+
>React : Symbol(React, Decl(file1.tsx, 0, 6))
31+
>PureComponent : Symbol(React.PureComponent, Decl(react.d.ts, 180, 5))
32+
33+
render() {
34+
>render : Symbol(RenderString.render, Decl(file1.tsx, 6, 58))
35+
36+
return (
37+
<>
38+
<MyComponent />
39+
>MyComponent : Symbol(MyComponent, Decl(file1.tsx, 1, 8))
40+
41+
<span>{ MY_STRING }</span>
42+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2461, 51))
43+
>MY_STRING : Symbol(MY_STRING, Decl(file1.tsx, 3, 5))
44+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2461, 51))
45+
46+
<span className={ MY_CLASSNAME } />
47+
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2461, 51))
48+
>className : Symbol(className, Decl(file1.tsx, 12, 13))
49+
>MY_CLASSNAME : Symbol(MY_CLASSNAME, Decl(file1.tsx, 4, 5))
50+
51+
</>
52+
)
53+
}
54+
}
55+
56+
export default RenderString
57+
>RenderString : Symbol(RenderString, Decl(file1.tsx, 4, 38))
58+
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
=== tests/cases/compiler/my-component.tsx ===
2+
declare var React: any;
3+
>React : any
4+
5+
export function MyComponent(props: any) {
6+
>MyComponent : (props: any) => JSX.Element
7+
>props : any
8+
9+
return <span>my component</span>;
10+
><span>my component</span> : JSX.Element
11+
>span : any
12+
>span : any
13+
}
14+
15+
=== tests/cases/compiler/file1.tsx ===
16+
import * as React from 'react'
17+
>React : typeof React
18+
19+
import { MyComponent } from './my-component'
20+
>MyComponent : (props: any) => JSX.Element
21+
22+
const MY_STRING: string = 'Ceci n\'est pas une string.'
23+
>MY_STRING : string
24+
>'Ceci n\'est pas une string.' : "Ceci n'est pas une string."
25+
26+
const MY_CLASSNAME: string = 'jeclass'
27+
>MY_CLASSNAME : string
28+
>'jeclass' : "jeclass"
29+
30+
class RenderString extends React.PureComponent<any, any> {
31+
>RenderString : RenderString
32+
>React.PureComponent : React.PureComponent<any, any>
33+
>React : typeof React
34+
>PureComponent : typeof React.PureComponent
35+
36+
render() {
37+
>render : () => JSX.Element
38+
39+
return (
40+
>( <> <MyComponent /> <span>{ MY_STRING }</span> <span className={ MY_CLASSNAME } /> </> ) : JSX.Element
41+
42+
<>
43+
><> <MyComponent /> <span>{ MY_STRING }</span> <span className={ MY_CLASSNAME } /> </> : JSX.Element
44+
45+
<MyComponent />
46+
><MyComponent /> : JSX.Element
47+
>MyComponent : (props: any) => JSX.Element
48+
49+
<span>{ MY_STRING }</span>
50+
><span>{ MY_STRING }</span> : JSX.Element
51+
>span : any
52+
>MY_STRING : string
53+
>span : any
54+
55+
<span className={ MY_CLASSNAME } />
56+
><span className={ MY_CLASSNAME } /> : JSX.Element
57+
>span : any
58+
>className : string
59+
>MY_CLASSNAME : string
60+
61+
</>
62+
)
63+
}
64+
}
65+
66+
export default RenderString
67+
>RenderString : RenderString
68+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// @jsx: react
2+
// @noUnusedLocals: true
3+
// @noLib: true
4+
// @skipLibCheck: true
5+
// @libFiles: react.d.ts,lib.d.ts
6+
7+
// @Filename: my-component.tsx
8+
declare var React: any;
9+
10+
export function MyComponent(props: any) {
11+
return <span>my component</span>;
12+
}
13+
14+
// @Filename: file1.tsx
15+
16+
import * as React from 'react'
17+
import { MyComponent } from './my-component'
18+
19+
const MY_STRING: string = 'Ceci n\'est pas une string.'
20+
const MY_CLASSNAME: string = 'jeclass'
21+
22+
class RenderString extends React.PureComponent<any, any> {
23+
render() {
24+
return (
25+
<>
26+
<MyComponent />
27+
<span>{ MY_STRING }</span>
28+
<span className={ MY_CLASSNAME } />
29+
</>
30+
)
31+
}
32+
}
33+
34+
export default RenderString

0 commit comments

Comments
 (0)