Skip to content

Commit 48c5bcf

Browse files
committed
Correctly resolve qualfied JSX tag names
Fixes #7020
1 parent 8ae55b4 commit 48c5bcf

12 files changed

+58
-184
lines changed

src/compiler/checker.ts

Lines changed: 38 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8365,7 +8365,7 @@ namespace ts {
83658365
checkJsxOpeningLikeElement(node.openingElement);
83668366

83678367
// Perform resolution on the closing tag so that rename/go to definition/etc work
8368-
getJsxElementTagSymbol(node.closingElement);
8368+
getJsxTagSymbol(node.closingElement);
83698369

83708370
// Check children
83718371
for (const child of node.children) {
@@ -8475,77 +8475,53 @@ namespace ts {
84758475
return jsxTypes[name];
84768476
}
84778477

8478-
/// Given a JSX opening element or self-closing element, return the symbol of the property that the tag name points to if
8479-
/// this is an intrinsic tag. This might be a named
8480-
/// property of the IntrinsicElements interface, or its string indexer.
8481-
/// If this is a class-based tag (otherwise returns undefined), returns the symbol of the class
8482-
/// type or factory function.
8483-
/// Otherwise, returns unknownSymbol.
8484-
function getJsxElementTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
8478+
function getJsxTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
84858479
const links = getNodeLinks(node);
8486-
if (!links.resolvedSymbol) {
8487-
if (isJsxIntrinsicIdentifier(node.tagName)) {
8488-
links.resolvedSymbol = lookupIntrinsicTag(node);
8489-
}
8490-
else {
8491-
links.resolvedSymbol = lookupClassTag(node);
8492-
}
8480+
if (isJsxIntrinsicIdentifier(node.tagName)) {
8481+
return getIntrinsicTagSymbol(node);
84938482
}
8494-
return links.resolvedSymbol;
8483+
else {
8484+
return resolveEntityName(node.tagName, SymbolFlags.Value);
8485+
}
8486+
}
84958487

8496-
function lookupIntrinsicTag(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
8488+
/**
8489+
* Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic
8490+
* property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic
8491+
* string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement).
8492+
* May also return unknownSymbol if both of these lookups fail.
8493+
*/
8494+
function getIntrinsicTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
8495+
const links = getNodeLinks(node);
8496+
if (!links.resolvedSymbol) {
84978497
const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements);
84988498
if (intrinsicElementsType !== unknownType) {
84998499
// Property case
85008500
const intrinsicProp = getPropertyOfType(intrinsicElementsType, (<Identifier>node.tagName).text);
85018501
if (intrinsicProp) {
85028502
links.jsxFlags |= JsxFlags.IntrinsicNamedElement;
8503-
return intrinsicProp;
8503+
return links.resolvedSymbol = intrinsicProp;
85048504
}
85058505

85068506
// Intrinsic string indexer case
85078507
const indexSignatureType = getIndexTypeOfType(intrinsicElementsType, IndexKind.String);
85088508
if (indexSignatureType) {
85098509
links.jsxFlags |= JsxFlags.IntrinsicIndexedElement;
8510-
return intrinsicElementsType.symbol;
8510+
return links.resolvedSymbol = intrinsicElementsType.symbol;
85118511
}
85128512

85138513
// Wasn't found
85148514
error(node, Diagnostics.Property_0_does_not_exist_on_type_1, (<Identifier>node.tagName).text, "JSX." + JsxNames.IntrinsicElements);
8515-
return unknownSymbol;
8515+
return links.resolvedSymbol = unknownSymbol;
85168516
}
85178517
else {
85188518
if (compilerOptions.noImplicitAny) {
85198519
error(node, Diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements);
85208520
}
8521-
return unknownSymbol;
8522-
}
8523-
}
8524-
8525-
function lookupClassTag(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
8526-
const valueSymbol: Symbol = resolveJsxTagName(node);
8527-
8528-
// Look up the value in the current scope
8529-
if (valueSymbol && valueSymbol !== unknownSymbol) {
8530-
links.jsxFlags |= JsxFlags.ValueElement;
8531-
if (valueSymbol.flags & SymbolFlags.Alias) {
8532-
markAliasSymbolAsReferenced(valueSymbol);
8533-
}
8534-
}
8535-
8536-
return valueSymbol || unknownSymbol;
8537-
}
8538-
8539-
function resolveJsxTagName(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
8540-
if (node.tagName.kind === SyntaxKind.Identifier) {
8541-
const tag = <Identifier>node.tagName;
8542-
const sym = getResolvedSymbol(tag);
8543-
return sym.exportSymbol || sym;
8544-
}
8545-
else {
8546-
return checkQualifiedName(<QualifiedName>node.tagName).symbol;
8521+
return links.resolvedSymbol = unknownSymbol;
85478522
}
85488523
}
8524+
return links.resolvedSymbol;
85498525
}
85508526

85518527
/**
@@ -8554,17 +8530,8 @@ namespace ts {
85548530
* For example, in the element <MyClass>, the element instance type is `MyClass` (not `typeof MyClass`).
85558531
*/
85568532
function getJsxElementInstanceType(node: JsxOpeningLikeElement) {
8557-
// There is no such thing as an instance type for a non-class element. This
8558-
// line shouldn't be hit.
8559-
Debug.assert(!!(getNodeLinks(node).jsxFlags & JsxFlags.ValueElement), "Should not call getJsxElementInstanceType on non-class Element");
8533+
const valueType = checkExpression(node.tagName);
85608534

8561-
const classSymbol = getJsxElementTagSymbol(node);
8562-
if (classSymbol === unknownSymbol) {
8563-
// Couldn't find the class instance type. Error has already been issued
8564-
return anyType;
8565-
}
8566-
8567-
const valueType = getTypeOfSymbol(classSymbol);
85688535
if (isTypeAny(valueType)) {
85698536
// Short-circuit if the class tag is using an element type 'any'
85708537
return anyType;
@@ -8630,9 +8597,16 @@ namespace ts {
86308597
function getJsxElementAttributesType(node: JsxOpeningLikeElement): Type {
86318598
const links = getNodeLinks(node);
86328599
if (!links.resolvedJsxType) {
8633-
const sym = getJsxElementTagSymbol(node);
8634-
8635-
if (links.jsxFlags & JsxFlags.ValueElement) {
8600+
if (isJsxIntrinsicIdentifier(node.tagName)) {
8601+
const symbol = getIntrinsicTagSymbol(node);
8602+
if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) {
8603+
return links.resolvedJsxType = getTypeOfSymbol(symbol);
8604+
}
8605+
else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) {
8606+
return links.resolvedJsxType = getIndexInfoOfSymbol(symbol, IndexKind.String).type;
8607+
}
8608+
}
8609+
else {
86368610
// Get the element instance type (the result of newing or invoking this tag)
86378611
const elemInstanceType = getJsxElementInstanceType(node);
86388612

@@ -8641,7 +8615,7 @@ namespace ts {
86418615
if (!elemClassType || !isTypeAssignableTo(elemInstanceType, elemClassType)) {
86428616
// Is this is a stateless function component? See if its single signature's return type is
86438617
// assignable to the JSX Element Type
8644-
const elemType = getTypeOfSymbol(sym);
8618+
const elemType = checkExpression(node.tagName);
86458619
const callSignatures = elemType && getSignaturesOfType(elemType, SignatureKind.Call);
86468620
const callSignature = callSignatures && callSignatures.length > 0 && callSignatures[0];
86478621
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
@@ -8715,16 +8689,8 @@ namespace ts {
87158689
}
87168690
}
87178691
}
8718-
else if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) {
8719-
return links.resolvedJsxType = getTypeOfSymbol(sym);
8720-
}
8721-
else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) {
8722-
return links.resolvedJsxType = getIndexInfoOfSymbol(sym, IndexKind.String).type;
8723-
}
8724-
else {
8725-
// Resolution failed, so we don't know
8726-
return links.resolvedJsxType = anyType;
8727-
}
8692+
8693+
return links.resolvedJsxType = unknownType;
87288694
}
87298695

87308696
return links.resolvedJsxType;
@@ -15431,7 +15397,8 @@ namespace ts {
1543115397
else if ((entityName.parent.kind === SyntaxKind.JsxOpeningElement) ||
1543215398
(entityName.parent.kind === SyntaxKind.JsxSelfClosingElement) ||
1543315399
(entityName.parent.kind === SyntaxKind.JsxClosingElement)) {
15434-
return getJsxElementTagSymbol(<JsxOpeningLikeElement>entityName.parent);
15400+
15401+
return getJsxTagSymbol(<JsxOpeningLikeElement>entityName.parent);
1543515402
}
1543615403
else if (isExpression(entityName)) {
1543715404
if (nodeIsMissing(entityName)) {

src/compiler/types.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,6 @@ namespace ts {
422422
IntrinsicNamedElement = 1 << 0,
423423
/** An element inferred from the string index signature of the JSX.IntrinsicElements interface */
424424
IntrinsicIndexedElement = 1 << 1,
425-
/** An element backed by a class, class-like, or function value */
426-
ValueElement = 1 << 2,
427-
/** Element resolution failed */
428-
UnknownElement = 1 << 4,
429425

430426
IntrinsicElement = IntrinsicNamedElement | IntrinsicIndexedElement,
431427
}

tests/baselines/reference/jsxEsprimaFbTestSuite.errors.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,29): error TS1005: '{'
44
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,57): error TS1109: Expression expected.
55
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(39,58): error TS1109: Expression expected.
66
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(41,1): error TS1003: Identifier expected.
7+
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(41,8): error TS2503: Cannot find namespace 'a'.
78
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(41,12): error TS2657: JSX expressions must have one parent element
9+
tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(43,10): error TS2503: Cannot find namespace 'a'.
810

911

10-
==== tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx (7 errors) ====
12+
==== tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx (9 errors) ====
1113
declare var React: any;
1214
declare var 日本語;
1315
declare var AbC_def;
@@ -61,10 +63,14 @@ tests/cases/conformance/jsx/jsxEsprimaFbTestSuite.tsx(41,12): error TS2657: JSX
6163
<a.b></a.b>;
6264
~
6365
!!! error TS1003: Identifier expected.
66+
~
67+
!!! error TS2503: Cannot find namespace 'a'.
6468
~
6569
!!! error TS2657: JSX expressions must have one parent element
6670

6771
<a.b.c></a.b.c>;
72+
~
73+
!!! error TS2503: Cannot find namespace 'a'.
6874

6975
(<div />) < x;
7076

tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(11,12): error TS2304:
2424
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(11,16): error TS1109: Expression expected.
2525
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(12,2): error TS2304: Cannot find name 'a'.
2626
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(12,5): error TS1003: Identifier expected.
27-
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(12,10): error TS2304: Cannot find name 'a'.
27+
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(12,10): error TS2503: Cannot find namespace 'a'.
2828
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(12,13): error TS1005: '>' expected.
2929
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(12,14): error TS2304: Cannot find name 'c'.
3030
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(12,16): error TS1109: Expression expected.
@@ -38,7 +38,7 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(14,8): error TS2304:
3838
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(14,10): error TS1109: Expression expected.
3939
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(15,2): error TS2304: Cannot find name 'a'.
4040
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(15,4): error TS1003: Identifier expected.
41-
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(15,7): error TS2304: Cannot find name 'a'.
41+
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(15,7): error TS2503: Cannot find namespace 'a'.
4242
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(15,9): error TS1003: Identifier expected.
4343
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(16,3): error TS1003: Identifier expected.
4444
tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(16,4): error TS2304: Cannot find name 'foo'.
@@ -135,7 +135,7 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS1005:
135135
~
136136
!!! error TS1003: Identifier expected.
137137
~
138-
!!! error TS2304: Cannot find name 'a'.
138+
!!! error TS2503: Cannot find namespace 'a'.
139139
~
140140
!!! error TS1005: '>' expected.
141141
~
@@ -166,7 +166,7 @@ tests/cases/conformance/jsx/jsxInvalidEsprimaTestSuite.tsx(35,21): error TS1005:
166166
~
167167
!!! error TS1003: Identifier expected.
168168
~
169-
!!! error TS2304: Cannot find name 'a'.
169+
!!! error TS2503: Cannot find namespace 'a'.
170170
~
171171
!!! error TS1003: Identifier expected.
172172
<a[foo]></a[foo]>;

tests/baselines/reference/jsxReactTestSuite.symbols

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,8 @@ var x =
178178
>constructor : Symbol(unknown)
179179

180180
<Namespace.Component />;
181-
>Component : Symbol(unknown)
182181

183182
<Namespace.DeepNamespace.Component />;
184-
>Component : Symbol(unknown)
185183

186184
<Component { ... x } y
187185
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))

tests/baselines/reference/tsxAttributeResolution12.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
tests/cases/conformance/jsx/file.tsx(26,10): error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'.
2+
tests/cases/conformance/jsx/file.tsx(29,10): error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'.
23

34

45
==== tests/cases/conformance/jsx/react.d.ts (0 errors) ====
@@ -15,7 +16,7 @@ tests/cases/conformance/jsx/file.tsx(26,10): error TS2324: Property 'reqd' is mi
1516
}
1617
}
1718

18-
==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
19+
==== tests/cases/conformance/jsx/file.tsx (2 errors) ====
1920

2021
declare class Component<P, S> {
2122
constructor(props?: P, context?: any);
@@ -47,5 +48,7 @@ tests/cases/conformance/jsx/file.tsx(26,10): error TS2324: Property 'reqd' is mi
4748

4849
// Should error
4950
var t2 = <TestMod.Test />;
51+
~~~~~~~~~~~~~~~~
52+
!!! error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'.
5053

5154

tests/baselines/reference/tsxAttributeResolution12.symbols

Lines changed: 0 additions & 47 deletions
This file was deleted.

tests/baselines/reference/tsxAttributeResolution12.types

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)