Skip to content

Commit 736d4b8

Browse files
author
Antanas Arvasevicius
committed
microsoft#13890 Implementing JSX.ElementType support
1 parent 501084a commit 736d4b8

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"
@@ -11899,7 +11900,7 @@ namespace ts {
1189911900

1190011901
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
1190111902
checkJsxOpeningLikeElement(node);
11902-
return jsxElementType || anyType;
11903+
return getJsxElementType(node);
1190311904
}
1190411905

1190511906
function checkJsxElement(node: JsxElement) {
@@ -11929,7 +11930,7 @@ namespace ts {
1192911930
}
1193011931
}
1193111932

11932-
return jsxElementType || anyType;
11933+
return getJsxElementType(node);
1193311934
}
1193411935

1193511936
/**
@@ -12071,7 +12072,7 @@ namespace ts {
1207112072
* element is not a class element, or the class element type cannot be determined, returns 'undefined'.
1207212073
* For example, in the element <MyClass>, the element instance type is `MyClass` (not `typeof MyClass`).
1207312074
*/
12074-
function getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type) {
12075+
function getJsxElementInstanceType(node: JsxOpeningLikeElement | JsxElement | JsxSelfClosingElement, valueType: Type) {
1207512076
Debug.assert(!(valueType.flags & TypeFlags.Union));
1207612077
if (isTypeAny(valueType)) {
1207712078
// Short-circuit if the class tag is using an element type 'any'
@@ -12084,8 +12085,11 @@ namespace ts {
1208412085
// No construct signatures, try call signatures
1208512086
signatures = getSignaturesOfType(valueType, SignatureKind.Call);
1208612087
if (signatures.length === 0) {
12088+
12089+
const tagName = (node.kind === SyntaxKind.JsxElement) ? node.openingElement.tagName : node.tagName;
12090+
1208712091
// 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));
1208912093
return unknownType;
1209012094
}
1209112095
}
@@ -12130,6 +12134,41 @@ namespace ts {
1213012134
}
1213112135
}
1213212136

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+
1213312172
/**
1213412173
* Given React element instance type and the class type, resolve the Jsx type
1213512174
* Pass elemType to handle individual type in the union typed element type.
@@ -12250,6 +12289,88 @@ namespace ts {
1225012289
}
1225112290
}
1225212291

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

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+
1228012422
/**
1228112423
* Given a JSX attribute, returns the symbol for the corresponds property
1228212424
* 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
@@ -2786,6 +2786,7 @@
27862786
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
27872787
jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
27882788
resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element
2789+
resolvedJsxElementType?: Type; // resolved element type of JSX openinglike element
27892790
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.
27902791
superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
27912792
switchTypes?: Type[]; // Cached array of switch case expression types

0 commit comments

Comments
 (0)