Skip to content

Commit cf8432c

Browse files
committed
Merge pull request microsoft#7030 from RyanCavanaugh/fix7020
Correctly resolve qualified JSX tag names
2 parents 35eff19 + ea2cdc7 commit cf8432c

16 files changed

Lines changed: 197 additions & 133 deletions

src/compiler/checker.ts

Lines changed: 38 additions & 72 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,52 @@ 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 {
8485-
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-
}
8478+
function getJsxTagSymbol(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
8479+
if (isJsxIntrinsicIdentifier(node.tagName)) {
8480+
return getIntrinsicTagSymbol(node);
84938481
}
8494-
return links.resolvedSymbol;
8482+
else {
8483+
return checkExpression(node.tagName).symbol;
8484+
}
8485+
}
84958486

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

85068505
// Intrinsic string indexer case
85078506
const indexSignatureType = getIndexTypeOfType(intrinsicElementsType, IndexKind.String);
85088507
if (indexSignatureType) {
85098508
links.jsxFlags |= JsxFlags.IntrinsicIndexedElement;
8510-
return intrinsicElementsType.symbol;
8509+
return links.resolvedSymbol = intrinsicElementsType.symbol;
85118510
}
85128511

85138512
// Wasn't found
85148513
error(node, Diagnostics.Property_0_does_not_exist_on_type_1, (<Identifier>node.tagName).text, "JSX." + JsxNames.IntrinsicElements);
8515-
return unknownSymbol;
8514+
return links.resolvedSymbol = unknownSymbol;
85168515
}
85178516
else {
85188517
if (compilerOptions.noImplicitAny) {
85198518
error(node, Diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements);
85208519
}
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;
8520+
return links.resolvedSymbol = unknownSymbol;
85478521
}
85488522
}
8523+
return links.resolvedSymbol;
85498524
}
85508525

85518526
/**
@@ -8554,17 +8529,8 @@ namespace ts {
85548529
* For example, in the element <MyClass>, the element instance type is `MyClass` (not `typeof MyClass`).
85558530
*/
85568531
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");
8532+
const valueType = checkExpression(node.tagName);
85608533

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);
85688534
if (isTypeAny(valueType)) {
85698535
// Short-circuit if the class tag is using an element type 'any'
85708536
return anyType;
@@ -8630,9 +8596,16 @@ namespace ts {
86308596
function getJsxElementAttributesType(node: JsxOpeningLikeElement): Type {
86318597
const links = getNodeLinks(node);
86328598
if (!links.resolvedJsxType) {
8633-
const sym = getJsxElementTagSymbol(node);
8634-
8635-
if (links.jsxFlags & JsxFlags.ValueElement) {
8599+
if (isJsxIntrinsicIdentifier(node.tagName)) {
8600+
const symbol = getIntrinsicTagSymbol(node);
8601+
if (links.jsxFlags & JsxFlags.IntrinsicNamedElement) {
8602+
return links.resolvedJsxType = getTypeOfSymbol(symbol);
8603+
}
8604+
else if (links.jsxFlags & JsxFlags.IntrinsicIndexedElement) {
8605+
return links.resolvedJsxType = getIndexInfoOfSymbol(symbol, IndexKind.String).type;
8606+
}
8607+
}
8608+
else {
86368609
// Get the element instance type (the result of newing or invoking this tag)
86378610
const elemInstanceType = getJsxElementInstanceType(node);
86388611

@@ -8641,7 +8614,7 @@ namespace ts {
86418614
if (!elemClassType || !isTypeAssignableTo(elemInstanceType, elemClassType)) {
86428615
// Is this is a stateless function component? See if its single signature's return type is
86438616
// assignable to the JSX Element Type
8644-
const elemType = getTypeOfSymbol(sym);
8617+
const elemType = checkExpression(node.tagName);
86458618
const callSignatures = elemType && getSignaturesOfType(elemType, SignatureKind.Call);
86468619
const callSignature = callSignatures && callSignatures.length > 0 && callSignatures[0];
86478620
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
@@ -8715,16 +8688,8 @@ namespace ts {
87158688
}
87168689
}
87178690
}
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-
}
8691+
8692+
return links.resolvedJsxType = unknownType;
87288693
}
87298694

87308695
return links.resolvedJsxType;
@@ -15431,7 +15396,8 @@ namespace ts {
1543115396
else if ((entityName.parent.kind === SyntaxKind.JsxOpeningElement) ||
1543215397
(entityName.parent.kind === SyntaxKind.JsxSelfClosingElement) ||
1543315398
(entityName.parent.kind === SyntaxKind.JsxClosingElement)) {
15434-
return getJsxElementTagSymbol(<JsxOpeningLikeElement>entityName.parent);
15399+
15400+
return getJsxTagSymbol(<JsxOpeningLikeElement>entityName.parent);
1543515401
}
1543615402
else if (isExpression(entityName)) {
1543715403
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/jsxReactTestSuite.symbols

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,9 @@ declare var hasOwnProperty:any;
5656
>div : Symbol(unknown)
5757

5858
<Component>{foo}<br />{bar}</Component>
59-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
6059
>foo : Symbol(foo, Decl(jsxReactTestSuite.tsx, 7, 11))
6160
>br : Symbol(unknown)
6261
>bar : Symbol(bar, Decl(jsxReactTestSuite.tsx, 8, 11))
63-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
6462

6563
<br />
6664
>br : Symbol(unknown)
@@ -70,20 +68,12 @@ declare var hasOwnProperty:any;
7068

7169

7270
<Composite>
73-
>Composite : Symbol(Composite, Decl(jsxReactTestSuite.tsx, 3, 11))
74-
7571
{this.props.children}
7672
</Composite>;
77-
>Composite : Symbol(Composite, Decl(jsxReactTestSuite.tsx, 3, 11))
7873

7974
<Composite>
80-
>Composite : Symbol(Composite, Decl(jsxReactTestSuite.tsx, 3, 11))
81-
8275
<Composite2 />
83-
>Composite2 : Symbol(Composite2, Decl(jsxReactTestSuite.tsx, 4, 11))
84-
8576
</Composite>;
86-
>Composite : Symbol(Composite, Decl(jsxReactTestSuite.tsx, 3, 11))
8777

8878
var x =
8979
>x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3))
@@ -174,78 +164,62 @@ var x =
174164
>hasOwnProperty : Symbol(unknown)
175165

176166
<Component constructor="foo" />;
177-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
178167
>constructor : Symbol(unknown)
179168

180169
<Namespace.Component />;
181-
>Component : Symbol(unknown)
182170

183171
<Namespace.DeepNamespace.Component />;
184-
>Component : Symbol(unknown)
185172

186173
<Component { ... x } y
187-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
188174
>x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3))
189175
>y : Symbol(unknown)
190176

191177
={2 } z />;
192178
>z : Symbol(unknown)
193179

194180
<Component
195-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
196-
197181
{...this.props} sound="moo" />;
198182
>sound : Symbol(unknown)
199183

200184
<font-face />;
201185
>font-face : Symbol(unknown)
202186

203187
<Component x={y} />;
204-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
205188
>x : Symbol(unknown)
206189
>y : Symbol(y, Decl(jsxReactTestSuite.tsx, 9, 11))
207190

208191
<x-component />;
209192
>x-component : Symbol(unknown)
210193

211194
<Component {...x} />;
212-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
213195
>x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3))
214196

215197
<Component { ...x } y={2} />;
216-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
217198
>x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3))
218199
>y : Symbol(unknown)
219200

220201
<Component { ... x } y={2} z />;
221-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
222202
>x : Symbol(x, Decl(jsxReactTestSuite.tsx, 10, 11), Decl(jsxReactTestSuite.tsx, 35, 3))
223203
>y : Symbol(unknown)
224204
>z : Symbol(unknown)
225205

226206
<Component x={1} {...y} />;
227-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
228207
>x : Symbol(unknown)
229208
>y : Symbol(y, Decl(jsxReactTestSuite.tsx, 9, 11))
230209

231210

232211
<Component x={1} y="2" {...z} {...z}><Child /></Component>;
233-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
234212
>x : Symbol(unknown)
235213
>y : Symbol(unknown)
236214
>z : Symbol(z, Decl(jsxReactTestSuite.tsx, 11, 11))
237215
>z : Symbol(z, Decl(jsxReactTestSuite.tsx, 11, 11))
238-
>Child : Symbol(Child, Decl(jsxReactTestSuite.tsx, 5, 11))
239-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
240216

241217
<Component x="1" {...(z = { y: 2 }, z)} z={3}>Text</Component>;
242-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
243218
>x : Symbol(unknown)
244219
>z : Symbol(z, Decl(jsxReactTestSuite.tsx, 11, 11))
245220
>y : Symbol(y, Decl(jsxReactTestSuite.tsx, 113, 27))
246221
>z : Symbol(z, Decl(jsxReactTestSuite.tsx, 11, 11))
247222
>z : Symbol(unknown)
248-
>Component : Symbol(Component, Decl(jsxReactTestSuite.tsx, 2, 11))
249223

250224

251225

tests/baselines/reference/reactNamespaceJSXEmit.symbols

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,16 @@ declare var x: any;
1717
>data : Symbol(unknown)
1818

1919
<Bar x={x} />;
20-
>Bar : Symbol(Bar, Decl(reactNamespaceJSXEmit.tsx, 3, 11))
2120
>x : Symbol(unknown)
2221
>x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11))
2322

2423
<x-component />;
2524
>x-component : Symbol(unknown)
2625

2726
<Bar {...x} />;
28-
>Bar : Symbol(Bar, Decl(reactNamespaceJSXEmit.tsx, 3, 11))
2927
>x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11))
3028

3129
<Bar { ...x } y={2} />;
32-
>Bar : Symbol(Bar, Decl(reactNamespaceJSXEmit.tsx, 3, 11))
3330
>x : Symbol(x, Decl(reactNamespaceJSXEmit.tsx, 4, 11))
3431
>y : Symbol(unknown)
3532

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
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; }'.
3+
4+
5+
==== tests/cases/conformance/jsx/react.d.ts (0 errors) ====
6+
7+
declare module JSX {
8+
interface Element { }
9+
interface IntrinsicElements {
10+
}
11+
interface ElementAttributesProperty {
12+
props;
13+
}
14+
interface IntrinsicAttributes {
15+
ref?: string;
16+
}
17+
}
18+
19+
==== tests/cases/conformance/jsx/file.tsx (2 errors) ====
20+
21+
declare class Component<P, S> {
22+
constructor(props?: P, context?: any);
23+
setState(f: (prevState: S, props: P) => S, callback?: () => any): void;
24+
setState(state: S, callback?: () => any): void;
25+
forceUpdate(callBack?: () => any): void;
26+
render(): JSX.Element;
27+
props: P;
28+
state: S;
29+
context: {};
30+
}
31+
32+
33+
interface ComponentClass<P> {
34+
new (props?: P, context?: any): Component<P, any>;
35+
}
36+
37+
declare module TestMod {
38+
interface TestClass extends ComponentClass<{reqd: any}> {
39+
}
40+
var Test: TestClass;
41+
}
42+
43+
// Errors correctly
44+
const T = TestMod.Test;
45+
var t1 = <T />;
46+
~~~~~
47+
!!! error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'.
48+
49+
// Should error
50+
var t2 = <TestMod.Test />;
51+
~~~~~~~~~~~~~~~~
52+
!!! error TS2324: Property 'reqd' is missing in type 'IntrinsicAttributes & { reqd: any; }'.
53+
54+

0 commit comments

Comments
 (0)