Skip to content

Commit 9b1ba8f

Browse files
Fix react-jsx spread children invalid emit (#46565)
* Fix react-jsx spread children invalid emit * Update Baselines and/or Applied Lint Fixes * Change childrenLength parameter -> isStaticChildren Co-authored-by: TypeScript Bot <[email protected]>
1 parent 3254358 commit 9b1ba8f

18 files changed

+653
-11
lines changed

src/compiler/transformers/jsx.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ namespace ts {
2626
return currentFileState.filenameDeclaration.name;
2727
}
2828

29-
function getJsxFactoryCalleePrimitive(childrenLength: number): "jsx" | "jsxs" | "jsxDEV" {
30-
return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : childrenLength > 1 ? "jsxs" : "jsx";
29+
function getJsxFactoryCalleePrimitive(isStaticChildren: boolean): "jsx" | "jsxs" | "jsxDEV" {
30+
return compilerOptions.jsx === JsxEmit.ReactJSXDev ? "jsxDEV" : isStaticChildren ? "jsxs" : "jsx";
3131
}
3232

33-
function getJsxFactoryCallee(childrenLength: number) {
34-
const type = getJsxFactoryCalleePrimitive(childrenLength);
33+
function getJsxFactoryCallee(isStaticChildren: boolean) {
34+
const type = getJsxFactoryCalleePrimitive(isStaticChildren);
3535
return getImplicitImportForName(type);
3636
}
3737

@@ -206,7 +206,7 @@ namespace ts {
206206

207207
function convertJsxChildrenToChildrenPropAssignment(children: readonly JsxChild[]) {
208208
const nonWhitespaceChildren = getSemanticJsxChildren(children);
209-
if (length(nonWhitespaceChildren) === 1) {
209+
if (length(nonWhitespaceChildren) === 1 && !(nonWhitespaceChildren[0] as JsxExpression).dotDotDotToken) {
210210
const result = transformJsxChildToExpression(nonWhitespaceChildren[0]);
211211
return result && factory.createPropertyAssignment("children", result);
212212
}
@@ -221,16 +221,33 @@ namespace ts {
221221
const attrs = keyAttr ? filter(node.attributes.properties, p => p !== keyAttr) : node.attributes.properties;
222222
const objectProperties = length(attrs) ? transformJsxAttributesToObjectProps(attrs, childrenProp) :
223223
factory.createObjectLiteralExpression(childrenProp ? [childrenProp] : emptyArray); // When there are no attributes, React wants {}
224-
return visitJsxOpeningLikeElementOrFragmentJSX(tagName, objectProperties, keyAttr, length(getSemanticJsxChildren(children || emptyArray)), isChild, location);
224+
return visitJsxOpeningLikeElementOrFragmentJSX(
225+
tagName,
226+
objectProperties,
227+
keyAttr,
228+
children || emptyArray,
229+
isChild,
230+
location
231+
);
225232
}
226233

227-
function visitJsxOpeningLikeElementOrFragmentJSX(tagName: Expression, objectProperties: Expression, keyAttr: JsxAttribute | undefined, childrenLength: number, isChild: boolean, location: TextRange) {
234+
function visitJsxOpeningLikeElementOrFragmentJSX(
235+
tagName: Expression,
236+
objectProperties: Expression,
237+
keyAttr: JsxAttribute | undefined,
238+
children: readonly JsxChild[],
239+
isChild: boolean,
240+
location: TextRange
241+
) {
242+
const nonWhitespaceChildren = getSemanticJsxChildren(children);
243+
const isStaticChildren =
244+
length(nonWhitespaceChildren) > 1 || !!(nonWhitespaceChildren[0] as JsxExpression)?.dotDotDotToken;
228245
const args: Expression[] = [tagName, objectProperties, !keyAttr ? factory.createVoidZero() : transformJsxAttributeInitializer(keyAttr.initializer)];
229246
if (compilerOptions.jsx === JsxEmit.ReactJSXDev) {
230247
const originalFile = getOriginalNode(currentSourceFile);
231248
if (originalFile && isSourceFile(originalFile)) {
232249
// isStaticChildren development flag
233-
args.push(childrenLength > 1 ? factory.createTrue() : factory.createFalse());
250+
args.push(isStaticChildren ? factory.createTrue() : factory.createFalse());
234251
// __source development flag
235252
const lineCol = getLineAndCharacterOfPosition(originalFile, location.pos);
236253
args.push(factory.createObjectLiteralExpression([
@@ -242,7 +259,10 @@ namespace ts {
242259
args.push(factory.createThis());
243260
}
244261
}
245-
const element = setTextRange(factory.createCallExpression(getJsxFactoryCallee(childrenLength), /*typeArguments*/ undefined, args), location);
262+
const element = setTextRange(
263+
factory.createCallExpression(getJsxFactoryCallee(isStaticChildren), /*typeArguments*/ undefined, args),
264+
location
265+
);
246266

247267
if (isChild) {
248268
startOnNewLine(element);
@@ -294,7 +314,7 @@ namespace ts {
294314
getImplicitJsxFragmentReference(),
295315
childrenProps || factory.createObjectLiteralExpression([]),
296316
/*keyAttr*/ undefined,
297-
length(getSemanticJsxChildren(children)),
317+
children,
298318
isChild,
299319
location
300320
);
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
tests/cases/conformance/jsx/tsxSpreadChildrenInvalidType.tsx(17,12): error TS2792: Cannot find module 'react/jsx-runtime'. Did you mean to set the 'moduleResolution' option to 'node', or to add aliases to the 'paths' option?
2+
tests/cases/conformance/jsx/tsxSpreadChildrenInvalidType.tsx(21,9): error TS2609: JSX spread child must be an array type.
3+
4+
5+
==== tests/cases/conformance/jsx/tsxSpreadChildrenInvalidType.tsx (2 errors) ====
6+
declare module JSX {
7+
interface Element { }
8+
interface IntrinsicElements {
9+
[s: string]: any;
10+
}
11+
}
12+
declare var React: any;
13+
14+
interface TodoProp {
15+
id: number;
16+
todo: string;
17+
}
18+
interface TodoListProps {
19+
todos: TodoProp[];
20+
}
21+
function Todo(prop: { key: number, todo: string }) {
22+
return <div>{prop.key.toString() + prop.todo}</div>;
23+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24+
!!! error TS2792: Cannot find module 'react/jsx-runtime'. Did you mean to set the 'moduleResolution' option to 'node', or to add aliases to the 'paths' option?
25+
}
26+
function TodoList({ todos }: TodoListProps) {
27+
return <div>
28+
{...<Todo key={todos[0].id} todo={todos[0].todo} />}
29+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30+
!!! error TS2609: JSX spread child must be an array type.
31+
</div>;
32+
}
33+
function TodoListNoError({ todos }: TodoListProps) {
34+
// any is not checked
35+
return <div>
36+
{...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
37+
</div>;
38+
}
39+
let x: TodoListProps;
40+
<TodoList {...x}/>
41+

0 commit comments

Comments
 (0)