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