@@ -356,6 +356,7 @@ namespace ts {
356
356
IntrinsicElements: "IntrinsicElements",
357
357
ElementClass: "ElementClass",
358
358
ElementAttributesPropertyNameContainer: "ElementAttributesProperty",
359
+ ElementTypePropertyNameContainer: "ElementTypeProperty",
359
360
Element: "Element",
360
361
IntrinsicAttributes: "IntrinsicAttributes",
361
362
IntrinsicClassAttributes: "IntrinsicClassAttributes"
@@ -11899,7 +11900,7 @@ namespace ts {
11899
11900
11900
11901
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
11901
11902
checkJsxOpeningLikeElement(node);
11902
- return jsxElementType || anyType ;
11903
+ return getJsxElementType(node) ;
11903
11904
}
11904
11905
11905
11906
function checkJsxElement(node: JsxElement) {
@@ -11929,7 +11930,7 @@ namespace ts {
11929
11930
}
11930
11931
}
11931
11932
11932
- return jsxElementType || anyType ;
11933
+ return getJsxElementType(node) ;
11933
11934
}
11934
11935
11935
11936
/**
@@ -12071,7 +12072,7 @@ namespace ts {
12071
12072
* element is not a class element, or the class element type cannot be determined, returns 'undefined'.
12072
12073
* For example, in the element <MyClass>, the element instance type is `MyClass` (not `typeof MyClass`).
12073
12074
*/
12074
- function getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type) {
12075
+ function getJsxElementInstanceType(node: JsxOpeningLikeElement | JsxElement | JsxSelfClosingElement , valueType: Type) {
12075
12076
Debug.assert(!(valueType.flags & TypeFlags.Union));
12076
12077
if (isTypeAny(valueType)) {
12077
12078
// Short-circuit if the class tag is using an element type 'any'
@@ -12084,8 +12085,11 @@ namespace ts {
12084
12085
// No construct signatures, try call signatures
12085
12086
signatures = getSignaturesOfType(valueType, SignatureKind.Call);
12086
12087
if (signatures.length === 0) {
12088
+
12089
+ const tagName = (node.kind === SyntaxKind.JsxElement) ? node.openingElement.tagName : node.tagName;
12090
+
12087
12091
// We found no signatures at all, which is an error
12088
- error(node. tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(node. tagName));
12092
+ error(tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(tagName));
12089
12093
return unknownType;
12090
12094
}
12091
12095
}
@@ -12130,6 +12134,41 @@ namespace ts {
12130
12134
}
12131
12135
}
12132
12136
12137
+ /// Returns a property name which type will be the JSX Element type
12138
+ /// or 'undefined' if ElementTypeProperty doesn't exist (then element type will be type of JSX.Element or any)
12139
+ /// or '' if it has 0 properties (element type will be class instance type)
12140
+ function getJsxElementTypePropertyName() {
12141
+ // JSX
12142
+ const jsxNamespace = getGlobalSymbol(JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/undefined);
12143
+ // JSX.ElementTypeProperty [symbol]
12144
+ const attribsPropTypeSym = jsxNamespace && getSymbol(jsxNamespace.exports, JsxNames.ElementTypePropertyNameContainer, SymbolFlags.Type);
12145
+ // JSX.ElementTypeProperty [type]
12146
+ const attribPropType = attribsPropTypeSym && getDeclaredTypeOfSymbol(attribsPropTypeSym);
12147
+ // The properties of JSX.ElementTypeProperty
12148
+ const attribProperties = attribPropType && getPropertiesOfType(attribPropType);
12149
+
12150
+ if (attribProperties) {
12151
+ // ElementTypeProperty has zero properties, so the element type will be the class instance type
12152
+ if (attribProperties.length === 0) {
12153
+ return "";
12154
+ }
12155
+ // ElementTypeProperty has one property, so the element type type will be the type of the corresponding
12156
+ // property of the class instance type
12157
+ else if (attribProperties.length === 1) {
12158
+ return attribProperties[0].name;
12159
+ }
12160
+ // More than one property on ElementTypeProperty is an error
12161
+ else {
12162
+ error(attribsPropTypeSym.declarations[0], Diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, JsxNames.ElementTypePropertyNameContainer);
12163
+ return undefined;
12164
+ }
12165
+ }
12166
+ else {
12167
+ // No interface exists, so the element type will be a type of JSX.Element or any
12168
+ return undefined;
12169
+ }
12170
+ }
12171
+
12133
12172
/**
12134
12173
* Given React element instance type and the class type, resolve the Jsx type
12135
12174
* Pass elemType to handle individual type in the union typed element type.
@@ -12250,6 +12289,88 @@ namespace ts {
12250
12289
}
12251
12290
}
12252
12291
12292
+ /**
12293
+ * Given React element instance type and the class type, resolve the Jsx element type
12294
+ * Pass elemType to handle individual type in the union typed element type.
12295
+ */
12296
+ function getResolvedJsxElementType(node: JsxElement | JsxSelfClosingElement, elemType?: Type, elemClassType?: Type): Type {
12297
+ const defaultJsxElementType = jsxElementType || anyType;
12298
+
12299
+ const propsName = getJsxElementTypePropertyName();
12300
+ if (propsName === undefined) {
12301
+ // There is no type ElementTypeProperty just return JSX.Element or 'any'
12302
+ return defaultJsxElementType;
12303
+ }
12304
+
12305
+ const tagName = (node.kind === SyntaxKind.JsxElement) ? node.openingElement.tagName : node.tagName;
12306
+
12307
+ if (!elemType) {
12308
+ elemType = checkExpression(tagName);
12309
+ }
12310
+
12311
+ if (elemType.flags & TypeFlags.Union) {
12312
+ const types = (<UnionOrIntersectionType>elemType).types;
12313
+ return getUnionType(map(types, type => {
12314
+ return getResolvedJsxElementType(node, type, elemClassType);
12315
+ }), /*subtypeReduction*/ true);
12316
+ }
12317
+
12318
+ // If the elemType is a string type, we have to return JSX.Element or 'any' to prevent an error downstream as we will try to find construct or call signature of the type
12319
+ if (elemType.flags & TypeFlags.String) {
12320
+ return defaultJsxElementType;
12321
+ }
12322
+ else if (elemType.flags & TypeFlags.StringLiteral) {
12323
+ return defaultJsxElementType;
12324
+ }
12325
+
12326
+ // Get the element instance type (the result of newing or invoking this tag)
12327
+ const elemInstanceType = getJsxElementInstanceType(node, elemType);
12328
+
12329
+ if (!elemClassType || !isTypeAssignableTo(elemInstanceType, elemClassType)) {
12330
+ // Is this is a stateless function component? See if its single signature's return type is
12331
+ // assignable to the JSX Element Type
12332
+ if (jsxElementType) {
12333
+ const callSignatures = elemType && getSignaturesOfType(elemType, SignatureKind.Call);
12334
+ const callSignature = callSignatures && callSignatures.length > 0 && callSignatures[0];
12335
+ const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
12336
+
12337
+ if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
12338
+ return callReturnType;
12339
+ }
12340
+ }
12341
+ }
12342
+
12343
+ if (isTypeAny(elemInstanceType)) {
12344
+ return defaultJsxElementType;
12345
+ }
12346
+
12347
+
12348
+ if (propsName === "") {
12349
+ // If there is no e.g. attribute member in ElementTypeProperty, use the element class type instead
12350
+ return elemInstanceType;
12351
+ }
12352
+ else {
12353
+ const attributesType = getTypeOfPropertyOfType(elemInstanceType, propsName);
12354
+
12355
+ if (!attributesType) {
12356
+ // There is no element property on this instance type return JSX.Element or 'any'
12357
+ return defaultJsxElementType;
12358
+ }
12359
+ else if (isTypeAny(attributesType) || (attributesType === unknownType)) {
12360
+ return defaultJsxElementType;
12361
+ }
12362
+ else if (attributesType.flags & TypeFlags.Union) {
12363
+ // Props cannot be a union type
12364
+ error(tagName, Diagnostics.JSX_element_attributes_type_0_may_not_be_a_union_type, typeToString(attributesType));
12365
+ return defaultJsxElementType;
12366
+ }
12367
+ else {
12368
+ return attributesType;
12369
+ }
12370
+ }
12371
+ }
12372
+
12373
+
12253
12374
/**
12254
12375
* Given an opening/self-closing element, get the 'element attributes type', i.e. the type that tells
12255
12376
* us which attributes are valid on a given element.
@@ -12277,6 +12398,27 @@ namespace ts {
12277
12398
return links.resolvedJsxType;
12278
12399
}
12279
12400
12401
+ /**
12402
+ * Given an jsx element, get the 'element type'
12403
+ */
12404
+ function getJsxElementType(node: JsxElement | JsxSelfClosingElement): Type {
12405
+ const links = getNodeLinks(node);
12406
+
12407
+ if (!links.resolvedJsxElementType) {
12408
+
12409
+ const tagName = (node.kind === SyntaxKind.JsxElement) ? node.openingElement.tagName : node.tagName;
12410
+
12411
+ if (isJsxIntrinsicIdentifier(tagName)) {
12412
+ return jsxElementType || anyType;
12413
+ }
12414
+ else {
12415
+ const elemClassType = getJsxGlobalElementClassType();
12416
+ return links.resolvedJsxElementType = getResolvedJsxElementType(node, undefined, elemClassType);
12417
+ }
12418
+ }
12419
+ return links.resolvedJsxElementType;
12420
+ }
12421
+
12280
12422
/**
12281
12423
* Given a JSX attribute, returns the symbol for the corresponds property
12282
12424
* of the element attributes type. Will return unknownSymbol for attributes
0 commit comments