Skip to content

[WIP] Using overload for JSX stateless function component #11871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 73 commits into from
Closed
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
618da6f
Rename resolvedJsxType to be resolvedJSXElementAttributesType for cla…
Sep 30, 2016
70ea640
Create JsxAttributes which is similar to an ObjectLiteralExpression
Oct 4, 2016
8d02f9c
Add JsxOpeningLikeElement to CallLikeExpression
Oct 4, 2016
07c60a3
Rename parameters in resolveJsxElementAttributesType function
Oct 4, 2016
aeb7a3a
Split get attributes of intrinsic element and custom element
Oct 10, 2016
d263318
Rename getJsxElementAttributesType to getAttributesTypeFromJsxOpening…
Oct 11, 2016
cd0aabd
Check JsxAttributes of an intrinsic element
Oct 4, 2016
9b8b854
Using overload resolution logic when trying to pick a signature of a …
Oct 13, 2016
6b0d648
Add and update comments
Oct 17, 2016
fab4a84
Clean up prototyping overload-react
Oct 17, 2016
790ce8c
Update react.d.ts that is used in testing
Oct 18, 2016
999a585
Update comment
Oct 18, 2016
6d283b0
Change the type of default attribute (without initializer) to be true…
Oct 18, 2016
2cb95fc
Add tests: using spread-type in React.Component
Oct 18, 2016
a7c405e
Update baselines: using spread-type in React.Component
Oct 18, 2016
8d2fcc0
Add tests: optional property and overwrite spread property
Oct 18, 2016
2dc82cd
Update baselines: optional property and overwrite spread property
Oct 18, 2016
2a3b43b
Update react lib file for testing
Oct 20, 2016
2f18a62
Update baselines from update library test file, react.d.ts
Oct 20, 2016
5d66909
Treat JSXAttribute as declaration; Update .types and .symbol baselines
Oct 20, 2016
979f645
Use contextual type to find-all-reference and goto-definition for JSX…
Oct 21, 2016
df34e29
Add "(attribute)" to be use in quick-info display for JSXAttribute
Oct 21, 2016
c38f6c1
Update comment why we using contextual type in goto-definition when i…
Oct 22, 2016
5b454e6
Update incorrect baseline
Oct 24, 2016
33a5d98
Add tsx quick-info tests
Oct 24, 2016
f79f6d7
Cache testing lib files so we don't have to create source file everytime
Oct 24, 2016
96a4311
Add tests for stateless-function overload
Oct 22, 2016
8bac89e
Disallow excess property when check jsx attributes
Oct 24, 2016
cf83268
Merge branch 'wip-statelessOverload' of https://github.com/Microsoft/…
Oct 24, 2016
9a1ed32
Return the latest tried candidate when choosing overload
Oct 24, 2016
40e443c
Correct pick signature when there is a hyphenated name attribute
Oct 26, 2016
5eb5000
Update stateless function overload tests to include hyphenated name a…
Oct 26, 2016
dd08cc6
Add stateless function overload baselines
Oct 26, 2016
ab50bf3
Add hyphenated name jsx attributes and update baseline
Oct 26, 2016
ce4709c
Add contextual type in stateless function overload and baselines
Oct 26, 2016
8e4a699
Update baselines
Oct 26, 2016
c441f9f
Clean up using overload resolution for stateless function component
Oct 26, 2016
1c4f710
Add tests and update baselines for stateless function component with …
Oct 27, 2016
fd0c54c
Fix linting error
Oct 27, 2016
699db36
Add quickinfo tests for stateless function with type arguments
Oct 27, 2016
5fae439
Add support for react quick-info
Oct 31, 2016
e182674
Add quick info test cases for stateless function component
Nov 1, 2016
df7af98
Update baselines when ask for type of default JSX attribute (no initi…
Nov 1, 2016
40b1138
Add completion fourslash tests
Nov 1, 2016
116c878
Test case for property used in destructuring variable declaration
sheetalkamat Nov 2, 2016
13e8f7f
Mark property referenced in the destructuring as referenced
sheetalkamat Nov 2, 2016
ab75ea7
module resolution: prefer locally defined ambient modules, reuse reso…
vladima Nov 2, 2016
e8c3d62
Lock tslint version to 4.0.0-dev.0, because 4.0.0-dev.1 complains abo…
Nov 3, 2016
83abd04
Correct assignability for keyof types and type parameters
ahejlsberg Nov 3, 2016
4019265
Update tests
ahejlsberg Nov 3, 2016
d9b0b63
Accept new baselines
ahejlsberg Nov 3, 2016
6a5de5d
Fix linting errors
ahejlsberg Nov 3, 2016
9705791
Merge pull request #12021 from Microsoft/tslint_version
Nov 3, 2016
48f2b78
Merge branch 'master' into keyofAndConstraints
ahejlsberg Nov 3, 2016
702efd5
Merge pull request #12026 from Microsoft/keyofAndConstraints
ahejlsberg Nov 3, 2016
c6a5a56
Get attributes completion for JSX statelss function component
Nov 3, 2016
0027cee
Update Jsx completion tests to be more restrictive
Nov 3, 2016
7cb51d3
Add Jsx singature helps tests
Nov 3, 2016
fb6846d
Correctly display stateless function in signature help
Nov 3, 2016
cbec19a
Ensure transformFlags are correct before visiting a node.
rbuckton Nov 4, 2016
1c004bf
Port #12027, #11980 and #11932 to master (#12037)
zhengbli Nov 4, 2016
defc053
Merge pull request #12039 from Microsoft/fix12010
rbuckton Nov 4, 2016
4a90614
Merge pull request #11998 from Microsoft/unusedProperty
sheetalkamat Nov 4, 2016
30afb33
Add goto-definition tests for JSX and object literal
Nov 4, 2016
0d4c9fa
Handle goto-definition of stateless function component
Nov 4, 2016
9a25fa9
Update Jsx completion tests
Nov 4, 2016
e188191
Add JSX rename and find all references tests
Nov 4, 2016
98062a9
Merge branch 'object-spread' into wip-statelessOverload
Nov 4, 2016
126260b
Update get attributes symbol arrays and report an error when spreadin…
Nov 4, 2016
ed4fead
add missing bind calls to properly set parent on token nodes (#12057)
vladima Nov 5, 2016
c52be4a
Merge branch 'master' into wip-statelessOverload
Nov 7, 2016
12bb35a
Merge branch 'object-spread' into wip-statelessOverload
Nov 7, 2016
3f32fa1
Update baselines. Only allow objec type in spread (e.g no generic)
Nov 7, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,7 @@ namespace ts {
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocRecordType:
case SyntaxKind.JsxAttributes:
return ContainerFlags.IsContainer;

case SyntaxKind.InterfaceDeclaration:
Expand Down Expand Up @@ -1402,6 +1403,7 @@ namespace ts {
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.JSDocRecordType:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JsxAttributes:
// Interface/Object-types always have their children added to the 'members' of
// their container. They are only accessible through an instance of their
// container, and are never in scope otherwise (even inside the body of the
Expand Down Expand Up @@ -1585,6 +1587,14 @@ namespace ts {
return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object");
}

function bindJsxAttributes(node: JsxAttributes) {
return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__jsxAttributes");
}

function bindJsxAttribute(node: JsxAttribute, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
}

function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) {
const symbol = createSymbol(symbolFlags, name);
addDeclarationToSymbol(symbol, node, symbolFlags);
Expand Down Expand Up @@ -1994,6 +2004,12 @@ namespace ts {
case SyntaxKind.ModuleDeclaration:
return bindModuleDeclaration(<ModuleDeclaration>node);

// Jsx-attributes
case SyntaxKind.JsxAttributes:
return bindJsxAttributes(<JsxAttributes>node);
case SyntaxKind.JsxAttribute:
return bindJsxAttribute(<JsxAttribute>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);

// Imports and exports
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.NamespaceImport:
Expand Down Expand Up @@ -3050,6 +3066,7 @@ namespace ts {
case SyntaxKind.JsxText:
case SyntaxKind.JsxClosingElement:
case SyntaxKind.JsxAttribute:
case SyntaxKind.JsxAttributes:
case SyntaxKind.JsxSpreadAttribute:
case SyntaxKind.JsxExpression:
// These nodes are Jsx syntax.
Expand Down
664 changes: 504 additions & 160 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,8 @@ const _super = (function (geti, seti) {
return emitJsxClosingElement(<JsxClosingElement>node);
case SyntaxKind.JsxAttribute:
return emitJsxAttribute(<JsxAttribute>node);
case SyntaxKind.JsxAttributes:
return emitJsxAttributes(<JsxAttributes>node);
case SyntaxKind.JsxSpreadAttribute:
return emitJsxSpreadAttribute(<JsxSpreadAttribute>node);
case SyntaxKind.JsxExpression:
Expand Down Expand Up @@ -1967,15 +1969,21 @@ const _super = (function (geti, seti) {
write("<");
emitJsxTagName(node.tagName);
write(" ");
emitList(node, node.attributes, ListFormat.JsxElementAttributes);
// We are checking here so we won't re-enter the emiting pipeline and emit extra sourcemap
if (node.attributes.properties && node.attributes.properties.length > 0) {
emit(node.attributes);
}
write("/>");
}

function emitJsxOpeningElement(node: JsxOpeningElement) {
write("<");
emitJsxTagName(node.tagName);
writeIfAny(node.attributes, " ");
emitList(node, node.attributes, ListFormat.JsxElementAttributes);
writeIfAny(node.attributes.properties, " ");
// We are checking here so we won't re-enter the emiting pipeline and emit extra sourcemap
if (node.attributes.properties && node.attributes.properties.length > 0) {
emit(node.attributes);
}
write(">");
}

Expand All @@ -1989,6 +1997,10 @@ const _super = (function (geti, seti) {
write(">");
}

function emitJsxAttributes(node: JsxAttributes) {
emitList(node, node.properties, ListFormat.JsxElementAttributes);
}

function emitJsxAttribute(node: JsxAttribute) {
emit(node.name);
emitWithPrefix("=", node.initializer);
Expand Down
26 changes: 20 additions & 6 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1237,28 +1237,28 @@ namespace ts {
return node;
}

export function createJsxSelfClosingElement(tagName: JsxTagNameExpression, attributes: JsxAttributeLike[], location?: TextRange) {
export function createJsxSelfClosingElement(tagName: JsxTagNameExpression, attributes: JsxAttributes, location?: TextRange) {
const node = <JsxSelfClosingElement>createNode(SyntaxKind.JsxSelfClosingElement, location);
node.tagName = tagName;
node.attributes = createNodeArray(attributes);
node.attributes = attributes;
return node;
}

export function updateJsxSelfClosingElement(node: JsxSelfClosingElement, tagName: JsxTagNameExpression, attributes: JsxAttributeLike[]) {
export function updateJsxSelfClosingElement(node: JsxSelfClosingElement, tagName: JsxTagNameExpression, attributes: JsxAttributes) {
if (node.tagName !== tagName || node.attributes !== attributes) {
return updateNode(createJsxSelfClosingElement(tagName, attributes, node), node);
}
return node;
}

export function createJsxOpeningElement(tagName: JsxTagNameExpression, attributes: JsxAttributeLike[], location?: TextRange) {
export function createJsxOpeningElement(tagName: JsxTagNameExpression, attributes: JsxAttributes, location?: TextRange) {
const node = <JsxOpeningElement>createNode(SyntaxKind.JsxOpeningElement, location);
node.tagName = tagName;
node.attributes = createNodeArray(attributes);
node.attributes = attributes;
return node;
}

export function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, attributes: JsxAttributeLike[]) {
export function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, attributes: JsxAttributes) {
if (node.tagName !== tagName || node.attributes !== attributes) {
return updateNode(createJsxOpeningElement(tagName, attributes, node), node);
}
Expand All @@ -1278,6 +1278,20 @@ namespace ts {
return node;
}

export function createJsxAttributes(properties: JsxAttributeLike[], location?: TextRange) {
const jsxAttributes = <JsxAttributes>createNode(SyntaxKind.JsxAttributes, location);
setEmitFlags(jsxAttributes, EmitFlags.NoSourceMap);
jsxAttributes.properties = createNodeArray(properties);
return jsxAttributes;
}

export function updateJsxAttributes(jsxAttributes: JsxAttributes, properties: JsxAttributeLike[]) {
if (jsxAttributes.properties !== properties) {
return updateNode(createJsxAttributes(properties, jsxAttributes), jsxAttributes);
}
return jsxAttributes;
}

export function createJsxAttribute(name: Identifier, initializer: StringLiteral | JsxExpression, location?: TextRange) {
const node = <JsxAttribute>createNode(SyntaxKind.JsxAttribute, location);
node.name = name;
Expand Down
12 changes: 10 additions & 2 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,9 @@ namespace ts {
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxOpeningElement:
return visitNode(cbNode, (<JsxOpeningLikeElement>node).tagName) ||
visitNodes(cbNodes, (<JsxOpeningLikeElement>node).attributes);
visitNode(cbNode, (<JsxOpeningLikeElement>node).attributes);
case SyntaxKind.JsxAttributes:
return visitNodes(cbNodes, (<JsxAttributes>node).properties);
case SyntaxKind.JsxAttribute:
return visitNode(cbNode, (<JsxAttribute>node).name) ||
visitNode(cbNode, (<JsxAttribute>node).initializer);
Expand Down Expand Up @@ -3795,14 +3797,20 @@ namespace ts {
return result;
}

function parseJsxAttributes(): JsxAttributes {
const jsxAttributes = <JsxAttributes>createNode(SyntaxKind.JsxAttributes);
jsxAttributes.properties = parseList(ParsingContext.JsxAttributes, parseJsxAttribute);
return finishNode(jsxAttributes);
}

function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement | JsxSelfClosingElement {
const fullStart = scanner.getStartPos();

parseExpected(SyntaxKind.LessThanToken);

const tagName = parseJsxElementName();
const attributes = parseJsxAttributes();

const attributes = parseList(ParsingContext.JsxAttributes, parseJsxAttribute);
let node: JsxOpeningLikeElement;

if (token() === SyntaxKind.GreaterThanToken) {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformers/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ namespace ts {
function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: JsxChild[], isChild: boolean, location: TextRange) {
const tagName = getTagName(node);
let objectProperties: Expression;
const attrs = node.attributes;
const attrs = node.attributes.properties;
if (attrs.length === 0) {
// When there are no attributes, React wants "null"
objectProperties = createNull();
Expand Down
36 changes: 22 additions & 14 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ namespace ts {
JsxOpeningElement,
JsxClosingElement,
JsxAttribute,
JsxAttributes,
JsxSpreadAttribute,
JsxExpression,

Expand Down Expand Up @@ -705,6 +706,7 @@ namespace ts {
// SyntaxKind.BindingElement
// SyntaxKind.Property
// SyntaxKind.PropertyAssignment
// SyntaxKind.JsxAttribute
// SyntaxKind.ShorthandPropertyAssignment
// SyntaxKind.EnumMember
// SyntaxKind.JSDocPropertyTag
Expand Down Expand Up @@ -1374,7 +1376,7 @@ namespace ts {
template: TemplateLiteral;
}

export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator;
export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement;

export interface AsExpression extends Expression {
kind: SyntaxKind.AsExpression;
Expand Down Expand Up @@ -1403,35 +1405,39 @@ namespace ts {
closingElement: JsxClosingElement;
}

/// Either the opening tag in a <Tag>...</Tag> pair, or the lone <Tag /> in a self-closing form
export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement;

export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute;

export type JsxTagNameExpression = PrimaryExpression | PropertyAccessExpression;

export interface JsxAttributes extends ObjectLiteralExpressionBase<JsxAttributeLike> {
}

/// The opening element of a <Tag>...</Tag> JsxElement
export interface JsxOpeningElement extends Expression {
kind: SyntaxKind.JsxOpeningElement;
tagName: JsxTagNameExpression;
attributes: NodeArray<JsxAttribute | JsxSpreadAttribute>;
attributes: JsxAttributes;
}

/// A JSX expression of the form <TagName attrs />
export interface JsxSelfClosingElement extends PrimaryExpression {
kind: SyntaxKind.JsxSelfClosingElement;
tagName: JsxTagNameExpression;
attributes: NodeArray<JsxAttribute | JsxSpreadAttribute>;
attributes: JsxAttributes;
}

/// Either the opening tag in a <Tag>...</Tag> pair, or the lone <Tag /> in a self-closing form
export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement;

export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute;

export interface JsxAttribute extends Node {
kind: SyntaxKind.JsxAttribute;
// @kind(SyntaxKind.JsxAttribute)
export interface JsxAttribute extends ObjectLiteralElement {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't JsxAttribute still need a kind: SyntaxKind.JsxAttribute; ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right. will fix that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also don't need the // @kind(...) any more

name: Identifier;
/// JSX attribute initializers are optional; <X y /> is sugar for <X y={true} />
initializer?: StringLiteral | JsxExpression;
}

export interface JsxSpreadAttribute extends Node {
// @kind(SyntaxKind.JsxSpreadAttribute)
export interface JsxSpreadAttribute extends ObjectLiteralElement {
kind: SyntaxKind.JsxSpreadAttribute;
expression: Expression;
}
Expand Down Expand Up @@ -2298,7 +2304,7 @@ namespace ts {
getAliasedSymbol(symbol: Symbol): Symbol;
getExportsOfModule(moduleSymbol: Symbol): Symbol[];

getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
getAllAttributesTypeFromJsxOpeningLikeElement(elementNode: JsxOpeningLikeElement): Type;
getJsxIntrinsicTagNames(): Symbol[];
isOptionalParameter(node: ParameterDeclaration): boolean;
getAmbientModules(): Symbol[];
Expand Down Expand Up @@ -2669,7 +2675,7 @@ namespace ts {
isVisible?: boolean; // Is this node visible
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element
resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element
hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt.
superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
switchTypes?: Type[]; // Cached array of switch case expression types
Expand Down Expand Up @@ -2704,6 +2710,8 @@ namespace ts {
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
/* @internal */
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
/* @internal */
JsxAttributes = 1 << 24, // Jsx attributes type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preliminary reaction is that JsxAttributes can't produces types that are very different from normal typescript types, so I expect that this flag is not really needed. But I still need to read the rest of the code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, it's because of the ObjectLiteralBase type. Hm.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea the reason I need this flag is because in checking "hasExcessProperties" jsx attributes have different requirement


/* @internal */
Nullable = Undefined | Null,
Expand Down Expand Up @@ -2866,7 +2874,7 @@ namespace ts {

/* @internal */
// Object literals are initially marked fresh. Freshness disappears following an assignment,
// before a type assertion, or when when an object literal's type is widened. The regular
// before a type assertion, or when an object literal's type is widened. The regular
// version of a fresh type is identical except for the TypeFlags.FreshObjectLiteral flag.
export interface FreshObjectLiteralType extends ResolvedType {
regularType: ResolvedType; // Regular version of fresh type
Expand Down
15 changes: 15 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,8 @@ namespace ts {

export function isCallLikeExpression(node: Node): node is CallLikeExpression {
switch (node.kind) {
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.TaggedTemplateExpression:
Expand All @@ -1087,6 +1089,9 @@ namespace ts {
if (node.kind === SyntaxKind.TaggedTemplateExpression) {
return (<TaggedTemplateExpression>node).tag;
}
else if (isJsxOpeningLikeElement(node)) {
return node.tagName;
}

// Will either be a CallExpression, NewExpression, or Decorator.
return (<CallExpression | Decorator>node).expression;
Expand Down Expand Up @@ -4132,6 +4137,7 @@ namespace ts {
|| kind === SyntaxKind.ImportEqualsDeclaration
|| kind === SyntaxKind.ImportSpecifier
|| kind === SyntaxKind.InterfaceDeclaration
|| kind === SyntaxKind.JsxAttribute
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.MethodSignature
|| kind === SyntaxKind.ModuleDeclaration
Expand Down Expand Up @@ -4244,6 +4250,11 @@ namespace ts {
|| kind === SyntaxKind.JsxText;
}

export function isJsxAttributes(node: Node): node is JsxAttributes {
const kind = node.kind;
return kind === SyntaxKind.JsxAttributes;
}

export function isJsxAttributeLike(node: Node): node is JsxAttributeLike {
const kind = node.kind;
return kind === SyntaxKind.JsxAttribute
Expand All @@ -4258,6 +4269,10 @@ namespace ts {
return node.kind === SyntaxKind.JsxAttribute;
}

export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement {
return node.kind === SyntaxKind.JsxOpeningElement || node.kind === SyntaxKind.JsxSelfClosingElement;
}

export function isStringLiteralOrJsxExpression(node: Node): node is StringLiteral | JsxExpression {
const kind = node.kind;
return kind === SyntaxKind.StringLiteral
Expand Down
14 changes: 11 additions & 3 deletions src/compiler/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,13 +461,17 @@ namespace ts {
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxOpeningElement:
result = reduceNode((<JsxSelfClosingElement | JsxOpeningElement>node).tagName, f, result);
result = reduceLeft((<JsxSelfClosingElement | JsxOpeningElement>node).attributes, f, result);
result = reduceNode((<JsxSelfClosingElement | JsxOpeningElement>node).attributes, f, result);
break;

case SyntaxKind.JsxClosingElement:
result = reduceNode((<JsxClosingElement>node).tagName, f, result);
break;

case SyntaxKind.JsxAttributes:
result = reduceLeft((<JsxAttributes>node).properties, f, result);
break;

case SyntaxKind.JsxAttribute:
result = reduceNode((<JsxAttribute>node).name, f, result);
result = reduceNode((<JsxAttribute>node).initializer, f, result);
Expand Down Expand Up @@ -1068,15 +1072,19 @@ namespace ts {
visitNodes((<JsxElement>node).children, visitor, isJsxChild),
visitNode((<JsxElement>node).closingElement, visitor, isJsxClosingElement));

case SyntaxKind.JsxAttributes:
return updateJsxAttributes(<JsxAttributes>node,
visitNodes((<JsxAttributes>node).properties, visitor, isJsxAttributeLike));

case SyntaxKind.JsxSelfClosingElement:
return updateJsxSelfClosingElement(<JsxSelfClosingElement>node,
visitNode((<JsxSelfClosingElement>node).tagName, visitor, isJsxTagNameExpression),
visitNodes((<JsxSelfClosingElement>node).attributes, visitor, isJsxAttributeLike));
visitNode((<JsxSelfClosingElement>node).attributes, visitor, isJsxAttributes));

case SyntaxKind.JsxOpeningElement:
return updateJsxOpeningElement(<JsxOpeningElement>node,
visitNode((<JsxOpeningElement>node).tagName, visitor, isJsxTagNameExpression),
visitNodes((<JsxOpeningElement>node).attributes, visitor, isJsxAttributeLike));
visitNode((<JsxOpeningElement>node).attributes, visitor, isJsxAttributes));

case SyntaxKind.JsxClosingElement:
return updateJsxClosingElement(<JsxClosingElement>node,
Expand Down
Loading