diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 76323f83e08..e70b969ca0a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8365,7 +8365,7 @@ namespace ts { checkJsxOpeningLikeElement(node.openingElement); // Perform resolution on the closing tag so that rename/go to definition/etc work - getJsxElementTagSymbol(node.closingElement); + getJsxTagSymbol(node.closingElement); // Check children for (const child of node.children) { @@ -8475,77 +8475,52 @@ namespace ts { return jsxTypes[name]; } - /// Given a JSX opening element or self-closing element, return the symbol of the property that the tag name points to if - /// this is an intrinsic tag. This might be a named - /// property of the IntrinsicElements interface, or its string indexer. - /// If this is a class-based tag (otherwise returns undefined), returns the symbol of the class - /// type or factory function. - /// Otherwise, returns unknownSymbol. - function getJsxElementTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol { + function getJsxTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol { + if (isJsxIntrinsicIdentifier(node.tagName)) { + return getIntrinsicTagSymbol(node); + } + else { + return checkExpression(node.tagName).symbol; + } + } + + /** + * Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic + * property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic + * string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement). + * May also return unknownSymbol if both of these lookups fail. + */ + function getIntrinsicTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol { const links = getNodeLinks(node); if (!links.resolvedSymbol) { - if (isJsxIntrinsicIdentifier(node.tagName)) { - links.resolvedSymbol = lookupIntrinsicTag(node); - } - else { - links.resolvedSymbol = lookupClassTag(node); - } - } - return links.resolvedSymbol; - - function lookupIntrinsicTag(node: JsxOpeningLikeElement | JsxClosingElement): Symbol { const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements); if (intrinsicElementsType !== unknownType) { // Property case const intrinsicProp = getPropertyOfType(intrinsicElementsType, (node.tagName).text); if (intrinsicProp) { links.jsxFlags |= JsxFlags.IntrinsicNamedElement; - return intrinsicProp; + return links.resolvedSymbol = intrinsicProp; } // Intrinsic string indexer case const indexSignatureType = getIndexTypeOfType(intrinsicElementsType, IndexKind.String); if (indexSignatureType) { links.jsxFlags |= JsxFlags.IntrinsicIndexedElement; - return intrinsicElementsType.symbol; + return links.resolvedSymbol = intrinsicElementsType.symbol; } // Wasn't found error(node, Diagnostics.Property_0_does_not_exist_on_type_1, (node.tagName).text, "JSX." + JsxNames.IntrinsicElements); - return unknownSymbol; + return links.resolvedSymbol = unknownSymbol; } else { if (compilerOptions.noImplicitAny) { error(node, Diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements); } - return unknownSymbol; - } - } - - function lookupClassTag(node: JsxOpeningLikeElement | JsxClosingElement): Symbol { - const valueSymbol: Symbol = resolveJsxTagName(node); - - // Look up the value in the current scope - if (valueSymbol && valueSymbol !== unknownSymbol) { - links.jsxFlags |= JsxFlags.ValueElement; - if (valueSymbol.flags & SymbolFlags.Alias) { - markAliasSymbolAsReferenced(valueSymbol); - } - } - - return valueSymbol || unknownSymbol; - } - - function resolveJsxTagName(node: JsxOpeningLikeElement | JsxClosingElement): Symbol { - if (node.tagName.kind === SyntaxKind.Identifier) { - const tag = node.tagName; - const sym = getResolvedSymbol(tag); - return sym.exportSymbol || sym; - } - else { - return checkQualifiedName(node.tagName).symbol; + return links.resolvedSymbol = unknownSymbol; } } + return links.resolvedSymbol; } /** @@ -8554,17 +8529,8 @@ namespace ts { * For example, in the element , the element instance type is `MyClass` (not `typeof MyClass`). */ function getJsxElementInstanceType(node: JsxOpeningLikeElement) { - // There is no such thing as an instance type for a non-class element. This - // line shouldn't be hit. - Debug.assert(!!(getNodeLinks(node).jsxFlags & JsxFlags.ValueElement), "Should not call getJsxElementInstanceType on non-class Element"); + const valueType = checkExpression(node.tagName); - const classSymbol = getJsxElementTagSymbol(node); - if (classSymbol === unknownSymbol) { - // Couldn't find the class instance type. Error has already been issued - return anyType; - } - - const valueType = getTypeOfSymbol(classSymbol); if (isTypeAny(valueType)) { // Short-circuit if the class tag is using an element type 'any' return anyType; @@ -8630,9 +8596,16 @@ namespace ts { function getJsxElementAttributesType(node: JsxOpeningLikeElement): Type { const links = getNodeLinks(node); if (!links.resolvedJsxType) { - const sym = getJsxElementTagSymbol(node); - - if (links.jsxFlags & JsxFlags.ValueElement) { + if (isJsxIntrinsicIdentifier(node.tagName)) { + const symbol = getIntrinsicTagSymbol(node); + if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) { + return links.resolvedJsxType = getTypeOfSymbol(symbol); + } + else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) { + return links.resolvedJsxType = getIndexInfoOfSymbol(symbol, IndexKind.String).type; + } + } + else { // Get the element instance type (the result of newing or invoking this tag) const elemInstanceType = getJsxElementInstanceType(node); @@ -8641,7 +8614,7 @@ namespace ts { if (!elemClassType || !isTypeAssignableTo(elemInstanceType, elemClassType)) { // Is this is a stateless function component? See if its single signature's return type is // assignable to the JSX Element Type - const elemType = getTypeOfSymbol(sym); + const elemType = checkExpression(node.tagName); const callSignatures = elemType && getSignaturesOfType(elemType, SignatureKind.Call); const callSignature = callSignatures && callSignatures.length > 0 && callSignatures[0]; const callReturnType = callSignature && getReturnTypeOfSignature(callSignature); @@ -8715,16 +8688,8 @@ namespace ts { } } } - else if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) { - return links.resolvedJsxType = getTypeOfSymbol(sym); - } - else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) { - return links.resolvedJsxType = getIndexInfoOfSymbol(sym, IndexKind.String).type; - } - else { - // Resolution failed, so we don't know - return links.resolvedJsxType = anyType; - } + + return links.resolvedJsxType = unknownType; } return links.resolvedJsxType; @@ -15431,7 +15396,8 @@ namespace ts { else if ((entityName.parent.kind === SyntaxKind.JsxOpeningElement) || (entityName.parent.kind === SyntaxKind.JsxSelfClosingElement) || (entityName.parent.kind === SyntaxKind.JsxClosingElement)) { - return getJsxElementTagSymbol(entityName.parent); + + return getJsxTagSymbol(entityName.parent); } else if (isExpression(entityName)) { if (nodeIsMissing(entityName)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 31448c2859c..6b9f49cac01 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -422,10 +422,6 @@ namespace ts { IntrinsicNamedElement = 1 << 0, /** An element inferred from the string index signature of the JSX.IntrinsicElements interface */ IntrinsicIndexedElement = 1 << 1, - /** An element backed by a class, class-like, or function value */ - ValueElement = 1 << 2, - /** Element resolution failed */ - UnknownElement = 1 << 4, IntrinsicElement = IntrinsicNamedElement | IntrinsicIndexedElement, } diff --git a/tests/baselines/reference/jsxReactTestSuite.symbols b/tests/baselines/reference/jsxReactTestSuite.symbols index cca1dae915b..1ee71370dbc 100644 --- a/tests/baselines/reference/jsxReactTestSuite.symbols +++ b/tests/baselines/reference/jsxReactTestSuite.symbols @@ -56,11 +56,9 @@ declare var hasOwnProperty:any; >div : Symbol(unknown) {foo}
{bar}
->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >foo : Symbol(foo, Decl(jsxReactTestSuite.tsx, 7, 11)) >br : Symbol(unknown) >bar : Symbol(bar, Decl(jsxReactTestSuite.tsx, 8, 11)) ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
>br : Symbol(unknown) @@ -70,20 +68,12 @@ declare var hasOwnProperty:any; ->Composite : Symbol(Composite, Decl(jsxReactTestSuite.tsx, 3, 11)) - {this.props.children} ; ->Composite : Symbol(Composite, Decl(jsxReactTestSuite.tsx, 3, 11)) ->Composite : Symbol(Composite, Decl(jsxReactTestSuite.tsx, 3, 11)) - ->Composite2 : Symbol(Composite2, Decl(jsxReactTestSuite.tsx, 4, 11)) - ; ->Composite : Symbol(Composite, Decl(jsxReactTestSuite.tsx, 3, 11)) var x = >x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3)) @@ -174,17 +164,13 @@ var x = >hasOwnProperty : Symbol(unknown) ; ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >constructor : Symbol(unknown) ; ->Component : Symbol(unknown) ; ->Component : Symbol(unknown) Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3)) >y : Symbol(unknown) @@ -192,8 +178,6 @@ var x = >z : Symbol(unknown) Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) - {...this.props} sound="moo" />; >sound : Symbol(unknown) @@ -201,7 +185,6 @@ var x = >font-face : Symbol(unknown) ; ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >x : Symbol(unknown) >y : Symbol(y, Decl(jsxReactTestSuite.tsx, 9, 11)) @@ -209,43 +192,34 @@ var x = >x-component : Symbol(unknown) ; ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3)) ; ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3)) >y : Symbol(unknown) ; ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3)) >y : Symbol(unknown) >z : Symbol(unknown) ; ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >x : Symbol(unknown) >y : Symbol(y, Decl(jsxReactTestSuite.tsx, 9, 11)) ; ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >x : Symbol(unknown) >y : Symbol(unknown) >z : Symbol(z, Decl(jsxReactTestSuite.tsx, 11, 11)) >z : Symbol(z, Decl(jsxReactTestSuite.tsx, 11, 11)) ->Child : Symbol(Child, Decl(jsxReactTestSuite.tsx, 5, 11)) ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) Text; ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) >x : Symbol(unknown) >z : Symbol(z, Decl(jsxReactTestSuite.tsx, 11, 11)) >y : Symbol(y, Decl(jsxReactTestSuite.tsx, 113, 27)) >z : Symbol(z, Decl(jsxReactTestSuite.tsx, 11, 11)) >z : Symbol(unknown) ->Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11)) diff --git a/tests/baselines/reference/reactNamespaceJSXEmit.symbols b/tests/baselines/reference/reactNamespaceJSXEmit.symbols index 3ca5b91e538..a0012cfd07a 100644 --- a/tests/baselines/reference/reactNamespaceJSXEmit.symbols +++ b/tests/baselines/reference/reactNamespaceJSXEmit.symbols @@ -17,7 +17,6 @@ declare var x: any; >data : Symbol(unknown) ; ->Bar : Symbol(Bar, Decl(reactNamespaceJSXEmit.tsx, 3, 11)) >x : Symbol(unknown) >x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11)) @@ -25,11 +24,9 @@ declare var x: any; >x-component : Symbol(unknown) ; ->Bar : Symbol(Bar, Decl(reactNamespaceJSXEmit.tsx, 3, 11)) >x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11)) ; ->Bar : Symbol(Bar, Decl(reactNamespaceJSXEmit.tsx, 3, 11)) >x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11)) >y : Symbol(unknown) diff --git a/tests/baselines/reference/tsxAttributeResolution12.errors.txt b/tests/baselines/reference/tsxAttributeResolution12.errors.txt new file mode 100644 index 00000000000..ecf51a0f3dd --- /dev/null +++ b/tests/baselines/reference/tsxAttributeResolution12.errors.txt @@ -0,0 +1,54 @@ +tests/cases/conformance/jsx/file.tsx(26,10): error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'. +tests/cases/conformance/jsx/file.tsx(29,10): error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'. + + +==== tests/cases/conformance/jsx/react.d.ts (0 errors) ==== + + declare module JSX { + interface Element { } + interface IntrinsicElements { + } + interface ElementAttributesProperty { + props; + } + interface IntrinsicAttributes { + ref?: string; + } + } + +==== tests/cases/conformance/jsx/file.tsx (2 errors) ==== + + declare class Component { + constructor(props?: P, context?: any); + setState(f: (prevState: S, props: P) => S, callback?: () => any): void; + setState(state: S, callback?: () => any): void; + forceUpdate(callBack?: () => any): void; + render(): JSX.Element; + props: P; + state: S; + context: {}; + } + + + interface ComponentClass

{ + new (props?: P, context?: any): Component; + } + + declare module TestMod { + interface TestClass extends ComponentClass<{reqd: any}> { + } + var Test: TestClass; + } + + // Errors correctly + const T = TestMod.Test; + var t1 = ; + ~~~~~ +!!! error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'. + + // Should error + var t2 = ; + ~~~~~~~~~~~~~~~~ +!!! error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'. + + \ No newline at end of file diff --git a/tests/baselines/reference/tsxAttributeResolution12.js b/tests/baselines/reference/tsxAttributeResolution12.js new file mode 100644 index 00000000000..05a854a7cbc --- /dev/null +++ b/tests/baselines/reference/tsxAttributeResolution12.js @@ -0,0 +1,55 @@ +//// [tests/cases/conformance/jsx/tsxAttributeResolution12.tsx] //// + +//// [react.d.ts] + +declare module JSX { + interface Element { } + interface IntrinsicElements { + } + interface ElementAttributesProperty { + props; + } + interface IntrinsicAttributes { + ref?: string; + } +} + +//// [file.tsx] + +declare class Component { + constructor(props?: P, context?: any); + setState(f: (prevState: S, props: P) => S, callback?: () => any): void; + setState(state: S, callback?: () => any): void; + forceUpdate(callBack?: () => any): void; + render(): JSX.Element; + props: P; + state: S; + context: {}; +} + + +interface ComponentClass

{ + new (props?: P, context?: any): Component; +} + +declare module TestMod { + interface TestClass extends ComponentClass<{reqd: any}> { + } + var Test: TestClass; +} + +// Errors correctly +const T = TestMod.Test; +var t1 = ; + +// Should error +var t2 = ; + + + +//// [file.jsx] +// Errors correctly +var T = TestMod.Test; +var t1 = ; +// Should error +var t2 = ; diff --git a/tests/baselines/reference/tsxElementResolution19.symbols b/tests/baselines/reference/tsxElementResolution19.symbols index 48aa4f962b6..87c48ccedf0 100644 --- a/tests/baselines/reference/tsxElementResolution19.symbols +++ b/tests/baselines/reference/tsxElementResolution19.symbols @@ -24,5 +24,5 @@ import {MyClass} from './file1'; >MyClass : Symbol(MyClass, Decl(file2.tsx, 3, 8)) ; ->MyClass : Symbol(MyClass, Decl(file2.tsx, 3, 8)) +>MyClass : Symbol(MyClass, Decl(file1.tsx, 2, 1)) diff --git a/tests/baselines/reference/tsxExternalModuleEmit1.symbols b/tests/baselines/reference/tsxExternalModuleEmit1.symbols index 129edcca0d1..79e20684b99 100644 --- a/tests/baselines/reference/tsxExternalModuleEmit1.symbols +++ b/tests/baselines/reference/tsxExternalModuleEmit1.symbols @@ -25,7 +25,7 @@ export class App extends React.Component { >render : Symbol(render, Decl(app.tsx, 5, 52)) return