diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab50ca652d30f..1df01284729be 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9628,8 +9628,9 @@ namespace ts { /** * Returns true iff React would emit this tag name as a string rather than an identifier or qualified name */ - function isJsxIntrinsicIdentifier(tagName: Identifier | QualifiedName) { - if (tagName.kind === SyntaxKind.QualifiedName) { + function isJsxIntrinsicIdentifier(tagName: JsxTagNameExpression) { + // TODO (yuisu): comment + if (tagName.kind === SyntaxKind.PropertyAccessExpression || tagName.kind === SyntaxKind.ThisKeyword) { return false; } else { @@ -9825,6 +9826,29 @@ namespace ts { })); } + // If the elemType is a string type, we have to return anyType to prevent an error downstream as we will try to find construct or call signature of the type + if (elemType.flags & TypeFlags.String) { + return anyType; + } + else if (elemType.flags & TypeFlags.StringLiteral) { + // If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type + const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements); + if (intrinsicElementsType !== unknownType) { + const stringLiteralTypeName = (elemType).text; + const intrinsicProp = getPropertyOfType(intrinsicElementsType, stringLiteralTypeName); + if (intrinsicProp) { + return getTypeOfSymbol(intrinsicProp); + } + const indexSignatureType = getIndexTypeOfType(intrinsicElementsType, IndexKind.String); + if (indexSignatureType) { + return indexSignatureType; + } + error(node, Diagnostics.Property_0_does_not_exist_on_type_1, stringLiteralTypeName, "JSX." + JsxNames.IntrinsicElements); + } + // If we need to report an error, we already done so here. So just return any to prevent any more error downstream + return anyType; + } + // Get the element instance type (the result of newing or invoking this tag) const elemInstanceType = getJsxElementInstanceType(node, elemType); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 101778e732223..a05e0c41b7fd1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1217,7 +1217,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function jsxEmitReact(node: JsxElement | JsxSelfClosingElement) { /// Emit a tag name, which is either '"div"' for lower-cased names, or /// 'Div' for upper-cased or dotted names - function emitTagName(name: Identifier | QualifiedName) { + function emitTagName(name: LeftHandSideExpression) { if (name.kind === SyntaxKind.Identifier && isIntrinsicJsxName((name).text)) { write('"'); emit(name); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b2f3755ec46be..29b9fa6088e41 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3575,7 +3575,7 @@ namespace ts { return finishNode(node); } - function tagNamesAreEquivalent(lhs: EntityName, rhs: EntityName): boolean { + function tagNamesAreEquivalent(lhs: JsxTagNameExpression, rhs: JsxTagNameExpression): boolean { if (lhs.kind !== rhs.kind) { return false; } @@ -3584,8 +3584,15 @@ namespace ts { return (lhs).text === (rhs).text; } - return (lhs).right.text === (rhs).right.text && - tagNamesAreEquivalent((lhs).left, (rhs).left); + if (lhs.kind === SyntaxKind.ThisKeyword) { + return true; + } + + // If we are at this statement then we must have PropertyAccessExpression and because tag name in Jsx element can only + // take forms of JsxTagNameExpression which includes an identifier, "this" expression, or another propertyAccessExpression + // it is safe to case the expression property as such. See parseJsxElementName for how we parse tag name in Jsx element + return (lhs).name.text === (rhs).name.text && + tagNamesAreEquivalent((lhs).expression as JsxTagNameExpression, (rhs).expression as JsxTagNameExpression); } @@ -3653,7 +3660,7 @@ namespace ts { Debug.fail("Unknown JSX child kind " + token); } - function parseJsxChildren(openingTagName: EntityName): NodeArray { + function parseJsxChildren(openingTagName: LeftHandSideExpression): NodeArray { const result = >[]; result.pos = scanner.getStartPos(); const saveParsingContext = parsingContext; @@ -3716,17 +3723,22 @@ namespace ts { return finishNode(node); } - function parseJsxElementName(): EntityName { + function parseJsxElementName(): JsxTagNameExpression { scanJsxIdentifier(); - let elementName: EntityName = parseIdentifierName(); + // JsxElement can have name in the form of + // propertyAccessExpression + // primaryExpression in the form of an identifier and "this" keyword + // We can't just simply use parseLeftHandSideExpressionOrHigher because then we will start consider class,function etc as a keyword + // We only want to consider "this" as a primaryExpression + let expression: JsxTagNameExpression = token === SyntaxKind.ThisKeyword ? + parseTokenNode() : parseIdentifierName(); while (parseOptional(SyntaxKind.DotToken)) { - scanJsxIdentifier(); - const node: QualifiedName = createNode(SyntaxKind.QualifiedName, elementName.pos); // !!! - node.left = elementName; - node.right = parseIdentifierName(); - elementName = finishNode(node); + const propertyAccess: PropertyAccessExpression = createNode(SyntaxKind.PropertyAccessExpression, expression.pos); + propertyAccess.expression = expression; + propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true); + expression = finishNode(propertyAccess); } - return elementName; + return expression; } function parseJsxExpression(inExpressionContext: boolean): JsxExpression { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f3549aa381530..8b6c51d8e5a39 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1042,11 +1042,13 @@ namespace ts { closingElement: JsxClosingElement; } + export type JsxTagNameExpression = PrimaryExpression | PropertyAccessExpression; + /// The opening element of a ... JsxElement // @kind(SyntaxKind.JsxOpeningElement) export interface JsxOpeningElement extends Expression { _openingElementBrand?: any; - tagName: EntityName; + tagName: JsxTagNameExpression; attributes: NodeArray; } @@ -1073,7 +1075,7 @@ namespace ts { // @kind(SyntaxKind.JsxClosingElement) export interface JsxClosingElement extends Node { - tagName: EntityName; + tagName: JsxTagNameExpression; } // @kind(SyntaxKind.JsxExpression) diff --git a/tests/baselines/reference/tsxDynamicTagName1.js b/tests/baselines/reference/tsxDynamicTagName1.js new file mode 100644 index 0000000000000..e77d126dd8320 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName1.js @@ -0,0 +1,8 @@ +//// [tsxDynamicTagName1.tsx] + +var CustomTag = "h1"; + Hello World // No error + +//// [tsxDynamicTagName1.jsx] +var CustomTag = "h1"; + Hello World ; // No error diff --git a/tests/baselines/reference/tsxDynamicTagName1.symbols b/tests/baselines/reference/tsxDynamicTagName1.symbols new file mode 100644 index 0000000000000..c3ba6440e4886 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName1.symbols @@ -0,0 +1,9 @@ +=== tests/cases/conformance/jsx/tsxDynamicTagName1.tsx === + +var CustomTag = "h1"; +>CustomTag : Symbol(CustomTag, Decl(tsxDynamicTagName1.tsx, 1, 3)) + + Hello World // No error +>CustomTag : Symbol(CustomTag, Decl(tsxDynamicTagName1.tsx, 1, 3)) +>CustomTag : Symbol(CustomTag, Decl(tsxDynamicTagName1.tsx, 1, 3)) + diff --git a/tests/baselines/reference/tsxDynamicTagName1.types b/tests/baselines/reference/tsxDynamicTagName1.types new file mode 100644 index 0000000000000..005afcf6adb8d --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName1.types @@ -0,0 +1,11 @@ +=== tests/cases/conformance/jsx/tsxDynamicTagName1.tsx === + +var CustomTag = "h1"; +>CustomTag : string +>"h1" : string + + Hello World // No error +> Hello World : any +>CustomTag : string +>CustomTag : string + diff --git a/tests/baselines/reference/tsxDynamicTagName2.errors.txt b/tests/baselines/reference/tsxDynamicTagName2.errors.txt new file mode 100644 index 0000000000000..008a013e379ff --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName2.errors.txt @@ -0,0 +1,19 @@ +tests/cases/conformance/jsx/tsxDynamicTagName2.tsx(10,1): error TS2339: Property 'customTag' does not exist on type 'JSX.IntrinsicElements'. +tests/cases/conformance/jsx/tsxDynamicTagName2.tsx(10,25): error TS2339: Property 'customTag' does not exist on type 'JSX.IntrinsicElements'. + + +==== tests/cases/conformance/jsx/tsxDynamicTagName2.tsx (2 errors) ==== + + declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + } + } + + var customTag = "h1"; + Hello World // This should be an error. The lower-case is look up as an intrinsic element name + ~~~~~~~~~~~ +!!! error TS2339: Property 'customTag' does not exist on type 'JSX.IntrinsicElements'. + ~~~~~~~~~~~~ +!!! error TS2339: Property 'customTag' does not exist on type 'JSX.IntrinsicElements'. \ No newline at end of file diff --git a/tests/baselines/reference/tsxDynamicTagName2.js b/tests/baselines/reference/tsxDynamicTagName2.js new file mode 100644 index 0000000000000..7fb7bb1a96563 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName2.js @@ -0,0 +1,15 @@ +//// [tsxDynamicTagName2.tsx] + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + } +} + +var customTag = "h1"; + Hello World // This should be an error. The lower-case is look up as an intrinsic element name + +//// [tsxDynamicTagName2.jsx] +var customTag = "h1"; + Hello World ; // This should be an error. The lower-case is look up as an intrinsic element name diff --git a/tests/baselines/reference/tsxDynamicTagName3.errors.txt b/tests/baselines/reference/tsxDynamicTagName3.errors.txt new file mode 100644 index 0000000000000..40c10c63f53da --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName3.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/jsx/tsxDynamicTagName3.tsx(10,1): error TS2339: Property 'h1' does not exist on type 'JSX.IntrinsicElements'. + + +==== tests/cases/conformance/jsx/tsxDynamicTagName3.tsx (1 errors) ==== + + declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + } + } + + var CustomTag: "h1" = "h1"; + Hello World // This should be an error. we will try look up string literal type in JSX.IntrinsicElements + ~~~~~~~~~~~ +!!! error TS2339: Property 'h1' does not exist on type 'JSX.IntrinsicElements'. \ No newline at end of file diff --git a/tests/baselines/reference/tsxDynamicTagName3.js b/tests/baselines/reference/tsxDynamicTagName3.js new file mode 100644 index 0000000000000..63c2a0a398b17 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName3.js @@ -0,0 +1,15 @@ +//// [tsxDynamicTagName3.tsx] + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + } +} + +var CustomTag: "h1" = "h1"; + Hello World // This should be an error. we will try look up string literal type in JSX.IntrinsicElements + +//// [tsxDynamicTagName3.jsx] +var CustomTag = "h1"; + Hello World ; // This should be an error. we will try look up string literal type in JSX.IntrinsicElements diff --git a/tests/baselines/reference/tsxDynamicTagName4.js b/tests/baselines/reference/tsxDynamicTagName4.js new file mode 100644 index 0000000000000..e708273237ddf --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName4.js @@ -0,0 +1,16 @@ +//// [tsxDynamicTagName4.tsx] + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + h1: any + } +} + +var CustomTag: "h1" = "h1"; + Hello World + +//// [tsxDynamicTagName4.jsx] +var CustomTag = "h1"; + Hello World ; diff --git a/tests/baselines/reference/tsxDynamicTagName4.symbols b/tests/baselines/reference/tsxDynamicTagName4.symbols new file mode 100644 index 0000000000000..3f0fe57ce1fe9 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName4.symbols @@ -0,0 +1,26 @@ +=== tests/cases/conformance/jsx/tsxDynamicTagName4.tsx === + +declare module JSX { +>JSX : Symbol(JSX, Decl(tsxDynamicTagName4.tsx, 0, 0)) + + interface Element { } +>Element : Symbol(Element, Decl(tsxDynamicTagName4.tsx, 1, 20)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(tsxDynamicTagName4.tsx, 2, 22)) + + div: any +>div : Symbol(IntrinsicElements.div, Decl(tsxDynamicTagName4.tsx, 3, 30)) + + h1: any +>h1 : Symbol(IntrinsicElements.h1, Decl(tsxDynamicTagName4.tsx, 4, 10)) + } +} + +var CustomTag: "h1" = "h1"; +>CustomTag : Symbol(CustomTag, Decl(tsxDynamicTagName4.tsx, 9, 3)) + + Hello World +>CustomTag : Symbol(CustomTag, Decl(tsxDynamicTagName4.tsx, 9, 3)) +>CustomTag : Symbol(CustomTag, Decl(tsxDynamicTagName4.tsx, 9, 3)) + diff --git a/tests/baselines/reference/tsxDynamicTagName4.types b/tests/baselines/reference/tsxDynamicTagName4.types new file mode 100644 index 0000000000000..3a9a34f289832 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName4.types @@ -0,0 +1,28 @@ +=== tests/cases/conformance/jsx/tsxDynamicTagName4.tsx === + +declare module JSX { +>JSX : any + + interface Element { } +>Element : Element + + interface IntrinsicElements { +>IntrinsicElements : IntrinsicElements + + div: any +>div : any + + h1: any +>h1 : any + } +} + +var CustomTag: "h1" = "h1"; +>CustomTag : "h1" +>"h1" : "h1" + + Hello World +> Hello World : JSX.Element +>CustomTag : "h1" +>CustomTag : "h1" + diff --git a/tests/baselines/reference/tsxDynamicTagName5.js b/tests/baselines/reference/tsxDynamicTagName5.js new file mode 100644 index 0000000000000..03c9627c2c195 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName5.js @@ -0,0 +1,41 @@ +//// [tests/cases/conformance/jsx/tsxDynamicTagName5.tsx] //// + +//// [react.d.ts] + +declare module 'react' { + class Component { } +} + +//// [app.tsx] +import * as React from 'react'; + +export class Text extends React.Component<{}, {}> { + _tagName: string = 'div'; + + render() { + return ( + + ); + } +} + +//// [app.jsx] +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var React = require('react'); +var Text = (function (_super) { + __extends(Text, _super); + function Text() { + _super.apply(this, arguments); + this._tagName = 'div'; + } + Text.prototype.render = function () { + return (); + }; + return Text; +}(React.Component)); +exports.Text = Text; diff --git a/tests/baselines/reference/tsxDynamicTagName5.symbols b/tests/baselines/reference/tsxDynamicTagName5.symbols new file mode 100644 index 0000000000000..9abac5521c9ad --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName5.symbols @@ -0,0 +1,34 @@ +=== tests/cases/conformance/jsx/react.d.ts === + +declare module 'react' { + class Component { } +>Component : Symbol(Component, Decl(react.d.ts, 1, 24)) +>T : Symbol(T, Decl(react.d.ts, 2, 17)) +>U : Symbol(U, Decl(react.d.ts, 2, 19)) +} + +=== tests/cases/conformance/jsx/app.tsx === +import * as React from 'react'; +>React : Symbol(React, Decl(app.tsx, 0, 6)) + +export class Text extends React.Component<{}, {}> { +>Text : Symbol(Text, Decl(app.tsx, 0, 31)) +>React.Component : Symbol(React.Component, Decl(react.d.ts, 1, 24)) +>React : Symbol(React, Decl(app.tsx, 0, 6)) +>Component : Symbol(React.Component, Decl(react.d.ts, 1, 24)) + + _tagName: string = 'div'; +>_tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) + + render() { +>render : Symbol(Text.render, Decl(app.tsx, 3, 27)) + + return ( + +>this._tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) +>this : Symbol(Text, Decl(app.tsx, 0, 31)) +>_tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) + + ); + } +} diff --git a/tests/baselines/reference/tsxDynamicTagName5.types b/tests/baselines/reference/tsxDynamicTagName5.types new file mode 100644 index 0000000000000..ac4ee31141aa3 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName5.types @@ -0,0 +1,38 @@ +=== tests/cases/conformance/jsx/react.d.ts === + +declare module 'react' { + class Component { } +>Component : Component +>T : T +>U : U +} + +=== tests/cases/conformance/jsx/app.tsx === +import * as React from 'react'; +>React : typeof React + +export class Text extends React.Component<{}, {}> { +>Text : Text +>React.Component : React.Component<{}, {}> +>React : typeof React +>Component : typeof React.Component + + _tagName: string = 'div'; +>_tagName : string +>'div' : string + + render() { +>render : () => any + + return ( +>( ) : any + + +> : any +>this._tagName : string +>this : this +>_tagName : string + + ); + } +} diff --git a/tests/baselines/reference/tsxDynamicTagName6.js b/tests/baselines/reference/tsxDynamicTagName6.js new file mode 100644 index 0000000000000..a7ec12b50b728 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName6.js @@ -0,0 +1,15 @@ +//// [tsxDynamicTagName6.tsx] + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + } +} + +const t = {tag:'h1'} +const foo = // No error + +//// [tsxDynamicTagName6.jsx] +var t = { tag: 'h1' }; +var foo = ; // No error diff --git a/tests/baselines/reference/tsxDynamicTagName6.symbols b/tests/baselines/reference/tsxDynamicTagName6.symbols new file mode 100644 index 0000000000000..f1afd91eacb99 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName6.symbols @@ -0,0 +1,26 @@ +=== tests/cases/conformance/jsx/tsxDynamicTagName6.tsx === + +declare module JSX { +>JSX : Symbol(JSX, Decl(tsxDynamicTagName6.tsx, 0, 0)) + + interface Element { } +>Element : Symbol(Element, Decl(tsxDynamicTagName6.tsx, 1, 20)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(tsxDynamicTagName6.tsx, 2, 22)) + + div: any +>div : Symbol(IntrinsicElements.div, Decl(tsxDynamicTagName6.tsx, 3, 30)) + } +} + +const t = {tag:'h1'} +>t : Symbol(t, Decl(tsxDynamicTagName6.tsx, 8, 5)) +>tag : Symbol(tag, Decl(tsxDynamicTagName6.tsx, 8, 11)) + +const foo = // No error +>foo : Symbol(foo, Decl(tsxDynamicTagName6.tsx, 9, 5)) +>t.tag : Symbol(tag, Decl(tsxDynamicTagName6.tsx, 8, 11)) +>t : Symbol(t, Decl(tsxDynamicTagName6.tsx, 8, 5)) +>tag : Symbol(tag, Decl(tsxDynamicTagName6.tsx, 8, 11)) + diff --git a/tests/baselines/reference/tsxDynamicTagName6.types b/tests/baselines/reference/tsxDynamicTagName6.types new file mode 100644 index 0000000000000..5dadf6fe825e9 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName6.types @@ -0,0 +1,29 @@ +=== tests/cases/conformance/jsx/tsxDynamicTagName6.tsx === + +declare module JSX { +>JSX : any + + interface Element { } +>Element : Element + + interface IntrinsicElements { +>IntrinsicElements : IntrinsicElements + + div: any +>div : any + } +} + +const t = {tag:'h1'} +>t : { tag: string; } +>{tag:'h1'} : { tag: string; } +>tag : string +>'h1' : string + +const foo = // No error +>foo : JSX.Element +> : JSX.Element +>t.tag : string +>t : { tag: string; } +>tag : string + diff --git a/tests/baselines/reference/tsxDynamicTagName7.errors.txt b/tests/baselines/reference/tsxDynamicTagName7.errors.txt new file mode 100644 index 0000000000000..19f9bed3c27de --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName7.errors.txt @@ -0,0 +1,23 @@ +tests/cases/conformance/jsx/app.tsx(8,8): error TS2604: JSX element type 'this' does not have any construct or call signatures. + + +==== tests/cases/conformance/jsx/react.d.ts (0 errors) ==== + + declare module 'react' { + class Component { } + } + +==== tests/cases/conformance/jsx/app.tsx (1 errors) ==== + import * as React from 'react'; + + export class Text extends React.Component<{}, {}> { + _tagName: string = 'div'; + + render() { + return ( + // this should be an error + ~~~~ +!!! error TS2604: JSX element type 'this' does not have any construct or call signatures. + ); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/tsxDynamicTagName7.js b/tests/baselines/reference/tsxDynamicTagName7.js new file mode 100644 index 0000000000000..f8dffbfad7e95 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName7.js @@ -0,0 +1,42 @@ +//// [tests/cases/conformance/jsx/tsxDynamicTagName7.tsx] //// + +//// [react.d.ts] + +declare module 'react' { + class Component { } +} + +//// [app.tsx] +import * as React from 'react'; + +export class Text extends React.Component<{}, {}> { + _tagName: string = 'div'; + + render() { + return ( + // this should be an error + ); + } +} + +//// [app.jsx] +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var React = require('react'); +var Text = (function (_super) { + __extends(Text, _super); + function Text() { + _super.apply(this, arguments); + this._tagName = 'div'; + } + Text.prototype.render = function () { + return ( // this should be an error + ); + }; + return Text; +}(React.Component)); +exports.Text = Text; diff --git a/tests/baselines/reference/tsxDynamicTagName8.js b/tests/baselines/reference/tsxDynamicTagName8.js new file mode 100644 index 0000000000000..36d551b0ce76b --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName8.js @@ -0,0 +1,41 @@ +//// [tests/cases/conformance/jsx/tsxDynamicTagName8.tsx] //// + +//// [react.d.ts] + +declare module 'react' { + class Component { } +} + +//// [app.tsx] +import * as React from 'react'; + +export class Text extends React.Component<{}, {}> { + _tagName: string = 'div'; + + render() { + return ( + Hello world + ); + } +} + +//// [app.jsx] +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var React = require('react'); +var Text = (function (_super) { + __extends(Text, _super); + function Text() { + _super.apply(this, arguments); + this._tagName = 'div'; + } + Text.prototype.render = function () { + return ( Hello world ); + }; + return Text; +}(React.Component)); +exports.Text = Text; diff --git a/tests/baselines/reference/tsxDynamicTagName8.symbols b/tests/baselines/reference/tsxDynamicTagName8.symbols new file mode 100644 index 0000000000000..87a71e740dee5 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName8.symbols @@ -0,0 +1,37 @@ +=== tests/cases/conformance/jsx/react.d.ts === + +declare module 'react' { + class Component { } +>Component : Symbol(Component, Decl(react.d.ts, 1, 24)) +>T : Symbol(T, Decl(react.d.ts, 2, 17)) +>U : Symbol(U, Decl(react.d.ts, 2, 19)) +} + +=== tests/cases/conformance/jsx/app.tsx === +import * as React from 'react'; +>React : Symbol(React, Decl(app.tsx, 0, 6)) + +export class Text extends React.Component<{}, {}> { +>Text : Symbol(Text, Decl(app.tsx, 0, 31)) +>React.Component : Symbol(React.Component, Decl(react.d.ts, 1, 24)) +>React : Symbol(React, Decl(app.tsx, 0, 6)) +>Component : Symbol(React.Component, Decl(react.d.ts, 1, 24)) + + _tagName: string = 'div'; +>_tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) + + render() { +>render : Symbol(Text.render, Decl(app.tsx, 3, 27)) + + return ( + Hello world +>this._tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) +>this : Symbol(Text, Decl(app.tsx, 0, 31)) +>_tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) +>this._tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) +>this : Symbol(Text, Decl(app.tsx, 0, 31)) +>_tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) + + ); + } +} diff --git a/tests/baselines/reference/tsxDynamicTagName8.types b/tests/baselines/reference/tsxDynamicTagName8.types new file mode 100644 index 0000000000000..154e7e33fe089 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName8.types @@ -0,0 +1,41 @@ +=== tests/cases/conformance/jsx/react.d.ts === + +declare module 'react' { + class Component { } +>Component : Component +>T : T +>U : U +} + +=== tests/cases/conformance/jsx/app.tsx === +import * as React from 'react'; +>React : typeof React + +export class Text extends React.Component<{}, {}> { +>Text : Text +>React.Component : React.Component<{}, {}> +>React : typeof React +>Component : typeof React.Component + + _tagName: string = 'div'; +>_tagName : string +>'div' : string + + render() { +>render : () => any + + return ( +>( Hello world ) : any + + Hello world +> Hello world : any +>this._tagName : string +>this : this +>_tagName : string +>this._tagName : string +>this : this +>_tagName : string + + ); + } +} diff --git a/tests/baselines/reference/tsxDynamicTagName9.js b/tests/baselines/reference/tsxDynamicTagName9.js new file mode 100644 index 0000000000000..78abc446e3f2f --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName9.js @@ -0,0 +1,41 @@ +//// [tests/cases/conformance/jsx/tsxDynamicTagName9.tsx] //// + +//// [react.d.ts] + +declare module 'react' { + class Component { } +} + +//// [app.tsx] +import * as React from 'react'; + +export class Text extends React.Component<{}, {}> { + _tagName: "div" = 'div'; + + render() { + return ( + Hello world + ); + } +} + +//// [app.jsx] +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var React = require('react'); +var Text = (function (_super) { + __extends(Text, _super); + function Text() { + _super.apply(this, arguments); + this._tagName = 'div'; + } + Text.prototype.render = function () { + return ( Hello world ); + }; + return Text; +}(React.Component)); +exports.Text = Text; diff --git a/tests/baselines/reference/tsxDynamicTagName9.symbols b/tests/baselines/reference/tsxDynamicTagName9.symbols new file mode 100644 index 0000000000000..436e12057519a --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName9.symbols @@ -0,0 +1,37 @@ +=== tests/cases/conformance/jsx/react.d.ts === + +declare module 'react' { + class Component { } +>Component : Symbol(Component, Decl(react.d.ts, 1, 24)) +>T : Symbol(T, Decl(react.d.ts, 2, 17)) +>U : Symbol(U, Decl(react.d.ts, 2, 19)) +} + +=== tests/cases/conformance/jsx/app.tsx === +import * as React from 'react'; +>React : Symbol(React, Decl(app.tsx, 0, 6)) + +export class Text extends React.Component<{}, {}> { +>Text : Symbol(Text, Decl(app.tsx, 0, 31)) +>React.Component : Symbol(React.Component, Decl(react.d.ts, 1, 24)) +>React : Symbol(React, Decl(app.tsx, 0, 6)) +>Component : Symbol(React.Component, Decl(react.d.ts, 1, 24)) + + _tagName: "div" = 'div'; +>_tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) + + render() { +>render : Symbol(Text.render, Decl(app.tsx, 3, 26)) + + return ( + Hello world +>this._tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) +>this : Symbol(Text, Decl(app.tsx, 0, 31)) +>_tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) +>this._tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) +>this : Symbol(Text, Decl(app.tsx, 0, 31)) +>_tagName : Symbol(Text._tagName, Decl(app.tsx, 2, 51)) + + ); + } +} diff --git a/tests/baselines/reference/tsxDynamicTagName9.types b/tests/baselines/reference/tsxDynamicTagName9.types new file mode 100644 index 0000000000000..e500d3f0ab136 --- /dev/null +++ b/tests/baselines/reference/tsxDynamicTagName9.types @@ -0,0 +1,41 @@ +=== tests/cases/conformance/jsx/react.d.ts === + +declare module 'react' { + class Component { } +>Component : Component +>T : T +>U : U +} + +=== tests/cases/conformance/jsx/app.tsx === +import * as React from 'react'; +>React : typeof React + +export class Text extends React.Component<{}, {}> { +>Text : Text +>React.Component : React.Component<{}, {}> +>React : typeof React +>Component : typeof React.Component + + _tagName: "div" = 'div'; +>_tagName : "div" +>'div' : "div" + + render() { +>render : () => any + + return ( +>( Hello world ) : any + + Hello world +> Hello world : any +>this._tagName : "div" +>this : this +>_tagName : "div" +>this._tagName : "div" +>this : this +>_tagName : "div" + + ); + } +} diff --git a/tests/baselines/reference/tsxUnionTypeComponent2.errors.txt b/tests/baselines/reference/tsxUnionTypeComponent2.errors.txt index e5a5c77da4c47..a43c45ec59eb2 100644 --- a/tests/baselines/reference/tsxUnionTypeComponent2.errors.txt +++ b/tests/baselines/reference/tsxUnionTypeComponent2.errors.txt @@ -5,9 +5,9 @@ tests/cases/conformance/jsx/file.tsx(8,2): error TS2604: JSX element type 'X' do import React = require('react'); - type Invalid1 = React.ComponentClass | string; + type Invalid1 = React.ComponentClass | number; - const X: Invalid1 = "Should fail to construct"; + const X: Invalid1 = 1; ; ~ diff --git a/tests/baselines/reference/tsxUnionTypeComponent2.js b/tests/baselines/reference/tsxUnionTypeComponent2.js index 18e86eb8b4c7a..b88816f285afe 100644 --- a/tests/baselines/reference/tsxUnionTypeComponent2.js +++ b/tests/baselines/reference/tsxUnionTypeComponent2.js @@ -2,9 +2,9 @@ import React = require('react'); -type Invalid1 = React.ComponentClass | string; +type Invalid1 = React.ComponentClass | number; -const X: Invalid1 = "Should fail to construct"; +const X: Invalid1 = 1; ; @@ -14,5 +14,5 @@ const X: Invalid1 = "Should fail to construct"; //// [file.js] "use strict"; var React = require('react'); -var X = "Should fail to construct"; +var X = 1; React.createElement(X, null); diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName1.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName1.tsx new file mode 100644 index 0000000000000..1a54b55f94e53 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName1.tsx @@ -0,0 +1,4 @@ +// @jsx: preserve + +var CustomTag = "h1"; + Hello World // No error \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName2.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName2.tsx new file mode 100644 index 0000000000000..0d5810bbd9a02 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName2.tsx @@ -0,0 +1,11 @@ +// @jsx: preserve + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + } +} + +var customTag = "h1"; + Hello World // This should be an error. The lower-case is look up as an intrinsic element name \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName3.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName3.tsx new file mode 100644 index 0000000000000..09e05800ca540 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName3.tsx @@ -0,0 +1,11 @@ +// @jsx: preserve + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + } +} + +var CustomTag: "h1" = "h1"; + Hello World // This should be an error. we will try look up string literal type in JSX.IntrinsicElements \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName4.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName4.tsx new file mode 100644 index 0000000000000..18ea11a66476b --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName4.tsx @@ -0,0 +1,12 @@ +// @jsx: preserve + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + h1: any + } +} + +var CustomTag: "h1" = "h1"; + Hello World \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName5.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName5.tsx new file mode 100644 index 0000000000000..b8e98cc110321 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName5.tsx @@ -0,0 +1,19 @@ +// @jsx: preserve + +//@filename: react.d.ts +declare module 'react' { + class Component { } +} + +//@filename: app.tsx +import * as React from 'react'; + +export class Text extends React.Component<{}, {}> { + _tagName: string = 'div'; + + render() { + return ( + + ); + } +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName6.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName6.tsx new file mode 100644 index 0000000000000..7657f7a82b307 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName6.tsx @@ -0,0 +1,11 @@ +// @jsx: preserve + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any + } +} + +const t = {tag:'h1'} +const foo = // No error \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName7.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName7.tsx new file mode 100644 index 0000000000000..52ff5f4ec2481 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName7.tsx @@ -0,0 +1,19 @@ +// @jsx: preserve + +//@filename: react.d.ts +declare module 'react' { + class Component { } +} + +//@filename: app.tsx +import * as React from 'react'; + +export class Text extends React.Component<{}, {}> { + _tagName: string = 'div'; + + render() { + return ( + // this should be an error + ); + } +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName8.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName8.tsx new file mode 100644 index 0000000000000..ee3bfaa0dfa8e --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName8.tsx @@ -0,0 +1,19 @@ +// @jsx: preserve + +//@filename: react.d.ts +declare module 'react' { + class Component { } +} + +//@filename: app.tsx +import * as React from 'react'; + +export class Text extends React.Component<{}, {}> { + _tagName: string = 'div'; + + render() { + return ( + Hello world + ); + } +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxDynamicTagName9.tsx b/tests/cases/conformance/jsx/tsxDynamicTagName9.tsx new file mode 100644 index 0000000000000..397fbdcd837e2 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxDynamicTagName9.tsx @@ -0,0 +1,19 @@ +// @jsx: preserve + +//@filename: react.d.ts +declare module 'react' { + class Component { } +} + +//@filename: app.tsx +import * as React from 'react'; + +export class Text extends React.Component<{}, {}> { + _tagName: "div" = 'div'; + + render() { + return ( + Hello world + ); + } +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxUnionTypeComponent2.tsx b/tests/cases/conformance/jsx/tsxUnionTypeComponent2.tsx index 3980721966115..dbd14f6c27f26 100644 --- a/tests/cases/conformance/jsx/tsxUnionTypeComponent2.tsx +++ b/tests/cases/conformance/jsx/tsxUnionTypeComponent2.tsx @@ -5,9 +5,9 @@ import React = require('react'); -type Invalid1 = React.ComponentClass | string; +type Invalid1 = React.ComponentClass | number; -const X: Invalid1 = "Should fail to construct"; +const X: Invalid1 = 1; ;