From 096c15b0dedeae3cc4cebf28748bb5f70b5e848c Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Fri, 17 Feb 2017 12:58:17 -0800 Subject: [PATCH] Defer get JSX.Element type allow null to be returned in SFC --- src/compiler/checker.ts | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 12b55aa818b..669a2e0f7c8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -278,6 +278,8 @@ namespace ts { let deferredGlobalAsyncIterableIteratorType: GenericType; let deferredGlobalTemplateStringsArrayType: ObjectType; let deferredJsxElementClassType: Type; + let deferredJsxElementType: Type; + let deferredJsxStatelessElementType: Type; let deferredNodes: Node[]; let deferredUnusedIdentifierNodes: Node[]; @@ -398,7 +400,6 @@ namespace ts { }); const typeofType = createTypeofType(); - let jsxElementType: Type; let _jsxNamespace: string; let _jsxFactoryEntity: EntityName; @@ -12306,12 +12307,12 @@ namespace ts { type.flags & TypeFlags.UnionOrIntersection && !forEach((type).types, t => !isValidSpreadType(t))); } - function checkJsxSelfClosingElement(node: JsxSelfClosingElement) { + function checkJsxSelfClosingElement(node: JsxSelfClosingElement): Type { checkJsxOpeningLikeElement(node); - return jsxElementType || anyType; + return getJsxGlobalElementType() || anyType; } - function checkJsxElement(node: JsxElement) { + function checkJsxElement(node: JsxElement): Type { // Check attributes checkJsxOpeningLikeElement(node.openingElement); @@ -12338,7 +12339,7 @@ namespace ts { } } - return jsxElementType || anyType; + return getJsxGlobalElementType() || anyType; } /** @@ -12579,13 +12580,14 @@ namespace ts { function defaultTryGetJsxStatelessFunctionAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type, elemInstanceType: Type, elementClassType?: Type): Type { Debug.assert(!(elementType.flags & TypeFlags.Union)); if (!elementClassType || !isTypeAssignableTo(elemInstanceType, elementClassType)) { - if (jsxElementType) { + const jsxStatelessElementType = getJsxGlobalStatelessElementType(); + if (jsxStatelessElementType) { // We don't call getResolvedSignature here because we have already resolve the type of JSX Element. const callSignature = getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, /*candidatesOutArray*/ undefined); if (callSignature !== unknownSignature) { const callReturnType = callSignature && getReturnTypeOfSignature(callSignature); let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0])); - if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) { + if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) { // Intersect in JSX.IntrinsicAttributes if it exists const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes); if (intrinsicAttributes !== unknownType) { @@ -12613,7 +12615,8 @@ namespace ts { Debug.assert(!(elementType.flags & TypeFlags.Union)); if (!elementClassType || !isTypeAssignableTo(elemInstanceType, elementClassType)) { // Is this is a stateless function component? See if its single signature's return type is assignable to the JSX Element Type - if (jsxElementType) { + const jsxStatelessElementType = getJsxGlobalStatelessElementType(); + if (jsxStatelessElementType) { // We don't call getResolvedSignature because here we have already resolve the type of JSX Element. const candidatesOutArray: Signature[] = []; getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, candidatesOutArray); @@ -12622,7 +12625,7 @@ namespace ts { for (const candidate of candidatesOutArray) { const callReturnType = getReturnTypeOfSignature(candidate); const paramType = callReturnType && (candidate.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(candidate.parameters[0])); - if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) { + if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) { let shouldBeCandidate = true; for (const attribute of openingLikeElement.attributes.properties) { if (isJsxAttribute(attribute) && @@ -12871,6 +12874,23 @@ namespace ts { return deferredJsxElementClassType; } + function getJsxGlobalElementType(): Type { + if (!deferredJsxElementType) { + deferredJsxElementType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.Element); + } + return deferredJsxElementType; + } + + function getJsxGlobalStatelessElementType(): Type { + if (!deferredJsxStatelessElementType) { + const jsxElementType = getJsxGlobalElementType(); + if (jsxElementType){ + deferredJsxStatelessElementType = getUnionType([jsxElementType, nullType]); + } + } + return deferredJsxStatelessElementType; + } + /** * Returns all the properties of the Jsx.IntrinsicElements interface */ @@ -12885,7 +12905,7 @@ namespace ts { error(errorNode, Diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided); } - if (jsxElementType === undefined) { + if (getJsxGlobalElementType() === undefined) { if (compilerOptions.noImplicitAny) { error(errorNode, Diagnostics.JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist); } @@ -21917,7 +21937,6 @@ namespace ts { globalNumberType = getGlobalType("Number", /*arity*/ 0, /*reportErrors*/ true); globalBooleanType = getGlobalType("Boolean", /*arity*/ 0, /*reportErrors*/ true); globalRegExpType = getGlobalType("RegExp", /*arity*/ 0, /*reportErrors*/ true); - jsxElementType = getExportedTypeFromNamespace("JSX", JsxNames.Element); anyArrayType = createArrayType(anyType); autoArrayType = createArrayType(autoType);