Skip to content

Commit 5231d21

Browse files
author
Antanas Arvasevicius
committed
#13890 Implementing JSX.ElementType support
1 parent a7728f8 commit 5231d21

File tree

2 files changed

+147
-4
lines changed

2 files changed

+147
-4
lines changed

Diff for: src/compiler/checker.ts

+146-4
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ namespace ts {
356356
IntrinsicElements: "IntrinsicElements",
357357
ElementClass: "ElementClass",
358358
ElementAttributesPropertyNameContainer: "ElementAttributesProperty",
359+
ElementTypePropertyNameContainer: "ElementTypeProperty",
359360
Element: "Element",
360361
IntrinsicAttributes: "IntrinsicAttributes",
361362
IntrinsicClassAttributes: "IntrinsicClassAttributes"
@@ -11901,7 +11902,7 @@ namespace ts {
1190111902

1190211903
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
1190311904
checkJsxOpeningLikeElement(node);
11904-
return jsxElementType || anyType;
11905+
return getJsxElementType(node);
1190511906
}
1190611907

1190711908
function checkJsxElement(node: JsxElement) {
@@ -11931,7 +11932,7 @@ namespace ts {
1193111932
}
1193211933
}
1193311934

11934-
return jsxElementType || anyType;
11935+
return getJsxElementType(node);
1193511936
}
1193611937

1193711938
/**
@@ -12073,7 +12074,7 @@ namespace ts {
1207312074
* element is not a class element, or the class element type cannot be determined, returns 'undefined'.
1207412075
* For example, in the element <MyClass>, the element instance type is `MyClass` (not `typeof MyClass`).
1207512076
*/
12076-
function getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type) {
12077+
function getJsxElementInstanceType(node: JsxOpeningLikeElement | JsxElement | JsxSelfClosingElement, valueType: Type) {
1207712078
Debug.assert(!(valueType.flags & TypeFlags.Union));
1207812079
if (isTypeAny(valueType)) {
1207912080
// Short-circuit if the class tag is using an element type 'any'
@@ -12086,8 +12087,11 @@ namespace ts {
1208612087
// No construct signatures, try call signatures
1208712088
signatures = getSignaturesOfType(valueType, SignatureKind.Call);
1208812089
if (signatures.length === 0) {
12090+
12091+
const tagName = (node.kind === SyntaxKind.JsxElement) ? node.openingElement.tagName : node.tagName;
12092+
1208912093
// 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));
1209112095
return unknownType;
1209212096
}
1209312097
}
@@ -12132,6 +12136,41 @@ namespace ts {
1213212136
}
1213312137
}
1213412138

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+
1213512174
/**
1213612175
* Given React element instance type and the class type, resolve the Jsx type
1213712176
* Pass elemType to handle individual type in the union typed element type.
@@ -12252,6 +12291,88 @@ namespace ts {
1225212291
}
1225312292
}
1225412293

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+
1225512376
/**
1225612377
* Given an opening/self-closing element, get the 'element attributes type', i.e. the type that tells
1225712378
* us which attributes are valid on a given element.
@@ -12279,6 +12400,27 @@ namespace ts {
1227912400
return links.resolvedJsxType;
1228012401
}
1228112402

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+
1228212424
/**
1228312425
* Given a JSX attribute, returns the symbol for the corresponds property
1228412426
* of the element attributes type. Will return unknownSymbol for attributes

Diff for: src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,7 @@
27882788
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
27892789
jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
27902790
resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element
2791+
resolvedJsxElementType?: Type; // resolved element type of JSX openinglike element
27912792
hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt.
27922793
superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
27932794
switchTypes?: Type[]; // Cached array of switch case expression types

0 commit comments

Comments
 (0)