Transform JSX spread children (#45693)

* Transform JSX spread children

* Target es2015 to skip helper and refine emit

Co-authored-by: Ron Buckton <ron.buckton@microsoft.com>

Co-authored-by: Ron Buckton <ron.buckton@microsoft.com>
Co-authored-by: Orta <git@orta.io>
This commit is contained in:
Jack Bates 2021-09-24 09:19:49 -07:00 committed by GitHub
parent af689cc5d5
commit 27bbdf1fe9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 335 additions and 15 deletions

View File

@ -547,7 +547,8 @@ namespace ts {
}
function visitJsxExpression(node: JsxExpression) {
return visitNode(node.expression, visitor, isExpression);
const expression = visitNode(node.expression, visitor, isExpression);
return node.dotDotDotToken ? factory.createSpreadElement(expression!) : expression;
}
}

View File

@ -89,31 +89,36 @@ const _brokenTree2 = <DOMSFC x={1} y={2}>{tree}{tree}</DOMSFC>
//// [component.js]
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var _this = this;
exports.__esModule = true;
exports.tree = exports.MyClass = exports.MySFC = void 0;
/** @jsx predom */
var renderer2_1 = require("./renderer2");
var MySFC = function (props) { return (0, renderer2_1.predom)("p", null,
props.x,
var MySFC = function (props) { return renderer2_1.predom.apply(void 0, __spreadArray(["p", null, props.x,
" + ",
props.y,
" = ",
props.x + props.y,
_this.props.children); };
props.x + props.y], _this.props.children, false)); };
exports.MySFC = MySFC;
var MyClass = /** @class */ (function () {
function MyClass(props) {
this.props = props;
}
MyClass.prototype.render = function () {
return (0, renderer2_1.predom)("p", null,
this.props.x,
return renderer2_1.predom.apply(void 0, __spreadArray(["p", null, this.props.x,
" + ",
this.props.y,
" = ",
this.props.x + this.props.y,
this.props.children);
this.props.x + this.props.y], this.props.children, false));
};
return MyClass;
}());
@ -124,6 +129,15 @@ exports.tree = (0, renderer2_1.predom)(exports.MySFC, { x: 1, y: 2 },
exports["default"] = (0, renderer2_1.predom)("h", null);
//// [index.js]
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
exports.__esModule = true;
/** @jsx dom */
var renderer_1 = require("./renderer");
@ -142,13 +156,11 @@ var DOMClass = /** @class */ (function () {
this.props = props;
}
DOMClass.prototype.render = function () {
return (0, renderer_1.dom)("p", null,
this.props.x,
return renderer_1.dom.apply(void 0, __spreadArray(["p", null, this.props.x,
" + ",
this.props.y,
" = ",
this.props.x + this.props.y,
this.props.children);
this.props.x + this.props.y], this.props.children, false));
};
return DOMClass;
}());

View File

@ -0,0 +1,47 @@
//// [tsxSpreadChildrenInvalidType.tsx]
declare module JSX {
interface Element { }
interface IntrinsicElements {
[s: string]: any;
}
}
declare var React: any;
interface TodoProp {
id: number;
todo: string;
}
interface TodoListProps {
todos: TodoProp[];
}
function Todo(prop: { key: number, todo: string }) {
return <div>{prop.key.toString() + prop.todo}</div>;
}
function TodoList({ todos }: TodoListProps) {
return <div>
{...<Todo key={todos[0].id} todo={todos[0].todo} />}
</div>;
}
function TodoListNoError({ todos }: TodoListProps) {
// any is not checked
return <div>
{...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
</div>;
}
let x: TodoListProps;
<TodoList {...x}/>
//// [tsxSpreadChildrenInvalidType.js]
function Todo(prop) {
return React.createElement("div", null, prop.key.toString() + prop.todo);
}
function TodoList({ todos }) {
return React.createElement("div", null, ...React.createElement(Todo, { key: todos[0].id, todo: todos[0].todo }));
}
function TodoListNoError({ todos }) {
// any is not checked
return React.createElement("div", null, ...React.createElement(Todo, { key: todos[0].id, todo: todos[0].todo }));
}
let x;
React.createElement(TodoList, Object.assign({}, x));

View File

@ -0,0 +1,38 @@
tests/cases/conformance/jsx/tsxSpreadChildrenInvalidType.tsx(21,9): error TS2609: JSX spread child must be an array type.
==== tests/cases/conformance/jsx/tsxSpreadChildrenInvalidType.tsx (1 errors) ====
declare module JSX {
interface Element { }
interface IntrinsicElements {
[s: string]: any;
}
}
declare var React: any;
interface TodoProp {
id: number;
todo: string;
}
interface TodoListProps {
todos: TodoProp[];
}
function Todo(prop: { key: number, todo: string }) {
return <div>{prop.key.toString() + prop.todo}</div>;
}
function TodoList({ todos }: TodoListProps) {
return <div>
{...<Todo key={todos[0].id} todo={todos[0].todo} />}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2609: JSX spread child must be an array type.
</div>;
}
function TodoListNoError({ todos }: TodoListProps) {
// any is not checked
return <div>
{...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
</div>;
}
let x: TodoListProps;
<TodoList {...x}/>

View File

@ -44,17 +44,26 @@ var __assign = (this && this.__assign) || function () {
};
return __assign.apply(this, arguments);
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
function Todo(prop) {
return React.createElement("div", null, prop.key.toString() + prop.todo);
}
function TodoList(_a) {
var todos = _a.todos;
return React.createElement("div", null, React.createElement(Todo, { key: todos[0].id, todo: todos[0].todo }));
return React.createElement.apply(React, __spreadArray(["div", null], React.createElement(Todo, { key: todos[0].id, todo: todos[0].todo }), false));
}
function TodoListNoError(_a) {
var todos = _a.todos;
// any is not checked
return React.createElement("div", null, React.createElement(Todo, { key: todos[0].id, todo: todos[0].todo }));
return React.createElement.apply(React, __spreadArray(["div", null], React.createElement(Todo, { key: todos[0].id, todo: todos[0].todo }), false));
}
var x;
React.createElement(TodoList, __assign({}, x));

View File

@ -0,0 +1,104 @@
=== tests/cases/conformance/jsx/tsxSpreadChildrenInvalidType.tsx ===
declare module JSX {
>JSX : Symbol(JSX, Decl(tsxSpreadChildrenInvalidType.tsx, 0, 0))
interface Element { }
>Element : Symbol(Element, Decl(tsxSpreadChildrenInvalidType.tsx, 0, 20))
interface IntrinsicElements {
>IntrinsicElements : Symbol(IntrinsicElements, Decl(tsxSpreadChildrenInvalidType.tsx, 1, 22))
[s: string]: any;
>s : Symbol(s, Decl(tsxSpreadChildrenInvalidType.tsx, 3, 3))
}
}
declare var React: any;
>React : Symbol(React, Decl(tsxSpreadChildrenInvalidType.tsx, 6, 11))
interface TodoProp {
>TodoProp : Symbol(TodoProp, Decl(tsxSpreadChildrenInvalidType.tsx, 6, 23))
id: number;
>id : Symbol(TodoProp.id, Decl(tsxSpreadChildrenInvalidType.tsx, 8, 20))
todo: string;
>todo : Symbol(TodoProp.todo, Decl(tsxSpreadChildrenInvalidType.tsx, 9, 15))
}
interface TodoListProps {
>TodoListProps : Symbol(TodoListProps, Decl(tsxSpreadChildrenInvalidType.tsx, 11, 1))
todos: TodoProp[];
>todos : Symbol(TodoListProps.todos, Decl(tsxSpreadChildrenInvalidType.tsx, 12, 25))
>TodoProp : Symbol(TodoProp, Decl(tsxSpreadChildrenInvalidType.tsx, 6, 23))
}
function Todo(prop: { key: number, todo: string }) {
>Todo : Symbol(Todo, Decl(tsxSpreadChildrenInvalidType.tsx, 14, 1))
>prop : Symbol(prop, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 14))
>key : Symbol(key, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 21))
>todo : Symbol(todo, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 34))
return <div>{prop.key.toString() + prop.todo}</div>;
>div : Symbol(JSX.IntrinsicElements, Decl(tsxSpreadChildrenInvalidType.tsx, 1, 22))
>prop.key.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>prop.key : Symbol(key, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 21))
>prop : Symbol(prop, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 14))
>key : Symbol(key, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 21))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>prop.todo : Symbol(todo, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 34))
>prop : Symbol(prop, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 14))
>todo : Symbol(todo, Decl(tsxSpreadChildrenInvalidType.tsx, 15, 34))
>div : Symbol(JSX.IntrinsicElements, Decl(tsxSpreadChildrenInvalidType.tsx, 1, 22))
}
function TodoList({ todos }: TodoListProps) {
>TodoList : Symbol(TodoList, Decl(tsxSpreadChildrenInvalidType.tsx, 17, 1))
>todos : Symbol(todos, Decl(tsxSpreadChildrenInvalidType.tsx, 18, 19))
>TodoListProps : Symbol(TodoListProps, Decl(tsxSpreadChildrenInvalidType.tsx, 11, 1))
return <div>
>div : Symbol(JSX.IntrinsicElements, Decl(tsxSpreadChildrenInvalidType.tsx, 1, 22))
{...<Todo key={todos[0].id} todo={todos[0].todo} />}
>Todo : Symbol(Todo, Decl(tsxSpreadChildrenInvalidType.tsx, 14, 1))
>key : Symbol(key, Decl(tsxSpreadChildrenInvalidType.tsx, 20, 17))
>todos[0].id : Symbol(TodoProp.id, Decl(tsxSpreadChildrenInvalidType.tsx, 8, 20))
>todos : Symbol(todos, Decl(tsxSpreadChildrenInvalidType.tsx, 18, 19))
>id : Symbol(TodoProp.id, Decl(tsxSpreadChildrenInvalidType.tsx, 8, 20))
>todo : Symbol(todo, Decl(tsxSpreadChildrenInvalidType.tsx, 20, 35))
>todos[0].todo : Symbol(TodoProp.todo, Decl(tsxSpreadChildrenInvalidType.tsx, 9, 15))
>todos : Symbol(todos, Decl(tsxSpreadChildrenInvalidType.tsx, 18, 19))
>todo : Symbol(TodoProp.todo, Decl(tsxSpreadChildrenInvalidType.tsx, 9, 15))
</div>;
>div : Symbol(JSX.IntrinsicElements, Decl(tsxSpreadChildrenInvalidType.tsx, 1, 22))
}
function TodoListNoError({ todos }: TodoListProps) {
>TodoListNoError : Symbol(TodoListNoError, Decl(tsxSpreadChildrenInvalidType.tsx, 22, 1))
>todos : Symbol(todos, Decl(tsxSpreadChildrenInvalidType.tsx, 23, 26))
>TodoListProps : Symbol(TodoListProps, Decl(tsxSpreadChildrenInvalidType.tsx, 11, 1))
// any is not checked
return <div>
>div : Symbol(JSX.IntrinsicElements, Decl(tsxSpreadChildrenInvalidType.tsx, 1, 22))
{...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
>Todo : Symbol(Todo, Decl(tsxSpreadChildrenInvalidType.tsx, 14, 1))
>key : Symbol(key, Decl(tsxSpreadChildrenInvalidType.tsx, 26, 18))
>todos[0].id : Symbol(TodoProp.id, Decl(tsxSpreadChildrenInvalidType.tsx, 8, 20))
>todos : Symbol(todos, Decl(tsxSpreadChildrenInvalidType.tsx, 23, 26))
>id : Symbol(TodoProp.id, Decl(tsxSpreadChildrenInvalidType.tsx, 8, 20))
>todo : Symbol(todo, Decl(tsxSpreadChildrenInvalidType.tsx, 26, 36))
>todos[0].todo : Symbol(TodoProp.todo, Decl(tsxSpreadChildrenInvalidType.tsx, 9, 15))
>todos : Symbol(todos, Decl(tsxSpreadChildrenInvalidType.tsx, 23, 26))
>todo : Symbol(TodoProp.todo, Decl(tsxSpreadChildrenInvalidType.tsx, 9, 15))
</div>;
>div : Symbol(JSX.IntrinsicElements, Decl(tsxSpreadChildrenInvalidType.tsx, 1, 22))
}
let x: TodoListProps;
>x : Symbol(x, Decl(tsxSpreadChildrenInvalidType.tsx, 29, 3))
>TodoListProps : Symbol(TodoListProps, Decl(tsxSpreadChildrenInvalidType.tsx, 11, 1))
<TodoList {...x}/>
>TodoList : Symbol(TodoList, Decl(tsxSpreadChildrenInvalidType.tsx, 17, 1))
>x : Symbol(x, Decl(tsxSpreadChildrenInvalidType.tsx, 29, 3))

View File

@ -0,0 +1,108 @@
=== tests/cases/conformance/jsx/tsxSpreadChildrenInvalidType.tsx ===
declare module JSX {
interface Element { }
interface IntrinsicElements {
[s: string]: any;
>s : string
}
}
declare var React: any;
>React : any
interface TodoProp {
id: number;
>id : number
todo: string;
>todo : string
}
interface TodoListProps {
todos: TodoProp[];
>todos : TodoProp[]
}
function Todo(prop: { key: number, todo: string }) {
>Todo : (prop: { key: number; todo: string;}) => JSX.Element
>prop : { key: number; todo: string; }
>key : number
>todo : string
return <div>{prop.key.toString() + prop.todo}</div>;
><div>{prop.key.toString() + prop.todo}</div> : JSX.Element
>div : any
>prop.key.toString() + prop.todo : string
>prop.key.toString() : string
>prop.key.toString : (radix?: number) => string
>prop.key : number
>prop : { key: number; todo: string; }
>key : number
>toString : (radix?: number) => string
>prop.todo : string
>prop : { key: number; todo: string; }
>todo : string
>div : any
}
function TodoList({ todos }: TodoListProps) {
>TodoList : ({ todos }: TodoListProps) => JSX.Element
>todos : TodoProp[]
return <div>
><div> {...<Todo key={todos[0].id} todo={todos[0].todo} />} </div> : JSX.Element
>div : any
{...<Todo key={todos[0].id} todo={todos[0].todo} />}
><Todo key={todos[0].id} todo={todos[0].todo} /> : JSX.Element
>Todo : (prop: { key: number; todo: string; }) => JSX.Element
>key : number
>todos[0].id : number
>todos[0] : TodoProp
>todos : TodoProp[]
>0 : 0
>id : number
>todo : string
>todos[0].todo : string
>todos[0] : TodoProp
>todos : TodoProp[]
>0 : 0
>todo : string
</div>;
>div : any
}
function TodoListNoError({ todos }: TodoListProps) {
>TodoListNoError : ({ todos }: TodoListProps) => JSX.Element
>todos : TodoProp[]
// any is not checked
return <div>
><div> {...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)} </div> : JSX.Element
>div : any
{...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
>(<Todo key={todos[0].id} todo={todos[0].todo} /> as any) : any
><Todo key={todos[0].id} todo={todos[0].todo} /> as any : any
><Todo key={todos[0].id} todo={todos[0].todo} /> : JSX.Element
>Todo : (prop: { key: number; todo: string; }) => JSX.Element
>key : number
>todos[0].id : number
>todos[0] : TodoProp
>todos : TodoProp[]
>0 : 0
>id : number
>todo : string
>todos[0].todo : string
>todos[0] : TodoProp
>todos : TodoProp[]
>0 : 0
>todo : string
</div>;
>div : any
}
let x: TodoListProps;
>x : TodoListProps
<TodoList {...x}/>
><TodoList {...x}/> : JSX.Element
>TodoList : ({ todos }: TodoListProps) => JSX.Element
>x : TodoListProps

View File

@ -1,4 +1,5 @@
// @jsx: react
// @target: es2015,es5
declare module JSX {
interface Element { }
interface IntrinsicElements {