Skip to content

Commit c51c496

Browse files
committed
Consolidate a little more duplicated logic into signature checking
1 parent 97df58e commit c51c496

File tree

1 file changed

+76
-88
lines changed

1 file changed

+76
-88
lines changed

src/compiler/checker.ts

+76-88
Original file line numberDiff line numberDiff line change
@@ -16567,6 +16567,9 @@ namespace ts {
1656716567
// If we're already in the process of resolving the given signature, don't resolve again as
1656816568
// that could cause infinite recursion. Instead, return anySignature.
1656916569
const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
16570+
if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
16571+
return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
16572+
}
1657016573
return getTypeAtPosition(signature, argIndex);
1657116574
}
1657216575

@@ -16934,7 +16937,11 @@ namespace ts {
1693416937
// (as below) instead!
1693516938
return node.parent.contextualType;
1693616939
}
16937-
return getAttributesTypeFromJsxOpeningLikeElement(node);
16940+
return getContextualTypeForArgumentAtIndex(node, 0);
16941+
}
16942+
16943+
function getEffectiveFirstArgumentForJsxSignature(signature: Signature, node: JsxOpeningLikeElement) {
16944+
return isJsxStatelessFunctionReference(node) ? getJsxPropsTypeFromCallSignature(signature, node) : getJsxPropsTypeFromClassType(signature, node);
1693816945
}
1693916946

1694016947
function getJsxPropsTypeFromCallSignature(sig: Signature, context: JsxOpeningLikeElement) {
@@ -16952,23 +16959,42 @@ namespace ts {
1695216959
return isTypeAny(instanceType) ? instanceType : getTypeOfPropertyOfType(instanceType, forcedLookupLocation);
1695316960
}
1695416961

16962+
function getStaticTypeOfReferencedJsxConstructor(context: JsxOpeningLikeElement) {
16963+
if (isJsxIntrinsicIdentifier(context.tagName)) {
16964+
const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(context);
16965+
const fakeSignature = createSignatureForJSXIntrinsic(context, result);
16966+
return getOrCreateTypeFromSignature(fakeSignature);
16967+
}
16968+
const tagType = checkExpressionCached(context.tagName);
16969+
if (tagType.flags & TypeFlags.StringLiteral) {
16970+
const result = getIntrinsicAttributesTypeFromStringLiteralType(tagType as StringLiteralType, context);
16971+
if (!result) {
16972+
return errorType;
16973+
}
16974+
const fakeSignature = createSignatureForJSXIntrinsic(context, result);
16975+
return getOrCreateTypeFromSignature(fakeSignature);
16976+
}
16977+
return tagType;
16978+
}
16979+
1695516980
function getJsxManagedAttributesFromLocatedAttributes(context: JsxOpeningLikeElement, ns: Symbol, attributesType: Type) {
1695616981
const managedSym = getJsxLibraryManagedAttributes(ns);
1695716982
if (managedSym) {
1695816983
const declaredManagedType = getDeclaredTypeOfSymbol(managedSym);
16984+
const ctorType = getStaticTypeOfReferencedJsxConstructor(context);
1695916985
if (length((declaredManagedType as GenericType).typeParameters) >= 2) {
16960-
const args = fillMissingTypeArguments([checkExpressionCached(context.tagName), attributesType], (declaredManagedType as GenericType).typeParameters, 2, isInJSFile(context));
16986+
const args = fillMissingTypeArguments([ctorType, attributesType], (declaredManagedType as GenericType).typeParameters, 2, isInJSFile(context));
1696116987
return createTypeReference((declaredManagedType as GenericType), args);
1696216988
}
1696316989
else if (length(declaredManagedType.aliasTypeArguments) >= 2) {
16964-
const args = fillMissingTypeArguments([checkExpressionCached(context.tagName), attributesType], declaredManagedType.aliasTypeArguments!, 2, isInJSFile(context));
16990+
const args = fillMissingTypeArguments([ctorType, attributesType], declaredManagedType.aliasTypeArguments!, 2, isInJSFile(context));
1696516991
return getTypeAliasInstantiation(declaredManagedType.aliasSymbol!, args);
1696616992
}
1696716993
}
1696816994
return attributesType;
1696916995
}
1697016996

16971-
function getJsxPropsTypeFromClassType(sig: Signature, isJs: boolean, context: JsxOpeningLikeElement) {
16997+
function getJsxPropsTypeFromClassType(sig: Signature, context: JsxOpeningLikeElement) {
1697216998
const ns = getJsxNamespaceAt(context);
1697316999
const forcedLookupLocation = getJsxElementPropertiesName(ns);
1697417000
let attributesType = forcedLookupLocation === undefined
@@ -17003,7 +17029,7 @@ namespace ts {
1700317029
const hostClassType = getReturnTypeOfSignature(sig);
1700417030
apparentAttributesType = intersectTypes(
1700517031
typeParams
17006-
? createTypeReference(<GenericType>intrinsicClassAttribs, fillMissingTypeArguments([hostClassType], typeParams, getMinTypeArgumentCount(typeParams), isJs))
17032+
? createTypeReference(<GenericType>intrinsicClassAttribs, fillMissingTypeArguments([hostClassType], typeParams, getMinTypeArgumentCount(typeParams), isInJSFile(context)))
1700717033
: intrinsicClassAttribs,
1700817034
apparentAttributesType
1700917035
);
@@ -17843,50 +17869,6 @@ namespace ts {
1784317869
return anyType;
1784417870
}
1784517871

17846-
/**
17847-
* Resolve attributes type of the given opening-like element. The attributes type is a type of attributes associated with the given elementType.
17848-
* For instance:
17849-
* declare function Foo(attr: { p1: string}): JSX.Element;
17850-
* <Foo p1={10} />; // This function will try resolve "Foo" and return an attributes type of "Foo" which is "{ p1: string }"
17851-
*
17852-
* The function is intended to initially be called from getAttributesTypeFromJsxOpeningLikeElement which already handle JSX-intrinsic-element..
17853-
* This function will try to resolve custom JSX attributes type in following order: string literal, stateless function, and stateful component
17854-
*
17855-
* @param openingLikeElement a non-intrinsic JSXOPeningLikeElement
17856-
* @param elementType an instance type of the given opening-like element. If undefined, the function will check type openinglikeElement's tagname.
17857-
* @return attributes type if able to resolve the type of node
17858-
* anyType if there is no type ElementAttributesProperty or there is an error
17859-
* emptyObjectType if there is no "prop" in the element instance type
17860-
*/
17861-
function resolveCustomJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type): Type {
17862-
if (elementType.flags & TypeFlags.Union) {
17863-
const types = (elementType as UnionType).types;
17864-
return getUnionType(types.map(type => {
17865-
return resolveCustomJsxElementAttributesType(openingLikeElement, type);
17866-
}), UnionReduction.Subtype);
17867-
}
17868-
17869-
// Shortcircuit any
17870-
if (isTypeAny(elementType)) {
17871-
return elementType;
17872-
}
17873-
// If the elemType is a string type, we have to return anyType to prevent an error downstream as we will try to find construct or call signature of the type
17874-
else if (elementType.flags & TypeFlags.String) {
17875-
return anyType;
17876-
}
17877-
else if (elementType.flags & TypeFlags.StringLiteral) {
17878-
return getIntrinsicAttributesTypeFromStringLiteralType(elementType as StringLiteralType, openingLikeElement) || anyType;
17879-
}
17880-
17881-
// Get the element instance type (the result of newing or invoking this tag)
17882-
const preferedOverload = getNodeLinks(openingLikeElement).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(openingLikeElement);
17883-
if (!preferedOverload) {
17884-
return errorType;
17885-
}
17886-
const isSFC = !length(getSignaturesOfType(elementType, SignatureKind.Construct));
17887-
return isSFC ? getJsxPropsTypeFromCallSignature(preferedOverload, openingLikeElement) : getJsxPropsTypeFromClassType(preferedOverload, isInJSFile(openingLikeElement), openingLikeElement);
17888-
}
17889-
1789017872
function checkJsxReturnAssignableToAppropriateBound(isSFC: boolean, elemInstanceType: Type, openingLikeElement: Node) {
1789117873
if (isSFC) {
1789217874
const sfcReturnConstraint = getJsxStatelessElementTypeAt(openingLikeElement);
@@ -17926,29 +17908,6 @@ namespace ts {
1792617908
return links.resolvedJsxElementAttributesType;
1792717909
}
1792817910

17929-
/**
17930-
* Get attributes type of the given custom opening-like JSX element.
17931-
* This function is intended to be called from a caller that handles intrinsic JSX element already.
17932-
* @param node a custom JSX opening-like element
17933-
*/
17934-
function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement): Type {
17935-
return resolveCustomJsxElementAttributesType(node, checkExpression(node.tagName));
17936-
}
17937-
17938-
/**
17939-
* Get the attributes type, which indicates the attributes that are valid on the given JSXOpeningLikeElement.
17940-
* @param node a JSXOpeningLikeElement node
17941-
* @return an attributes type of the given node
17942-
*/
17943-
function getAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type {
17944-
if (isJsxIntrinsicIdentifier(node.tagName)) {
17945-
return getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node);
17946-
}
17947-
else {
17948-
return getCustomJsxElementAttributesType(node);
17949-
}
17950-
}
17951-
1795217911
function getJsxElementClassTypeAt(location: Node): Type | undefined {
1795317912
const type = getJsxType(JsxNames.ElementClass, location);
1795417913
if (type === errorType) return undefined;
@@ -18900,8 +18859,7 @@ namespace ts {
1890018859
}
1890118860

1890218861
function inferJsxTypeArguments(node: JsxOpeningLikeElement, signature: Signature, excludeArgument: ReadonlyArray<boolean> | undefined, context: InferenceContext): Type[] {
18903-
const isCtor = !!length(getSignaturesOfType(checkExpression(node.tagName), SignatureKind.Construct));
18904-
const paramType = isCtor ? getJsxPropsTypeFromClassType(signature, isInJSFile(node), node) : getJsxPropsTypeFromCallSignature(signature, node);
18862+
const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node);
1890518863

1890618864
const checkAttrType = checkExpressionWithContextualType(node.attributes, paramType, excludeArgument && excludeArgument[0] !== undefined ? identityMapper : context);
1890718865
inferTypes(context.inferences, checkAttrType, paramType);
@@ -19046,7 +19004,7 @@ namespace ts {
1904619004

1904719005
function isJsxStatelessFunctionReference(node: JsxOpeningLikeElement) {
1904819006
if (isJsxIntrinsicIdentifier(node.tagName)) {
19049-
return false;
19007+
return true;
1905019008
}
1905119009
const tagType = checkExpression(node.tagName);
1905219010
return !length(getSignaturesOfType(getApparentType(tagType), SignatureKind.Construct));
@@ -19059,11 +19017,11 @@ namespace ts {
1905919017
* @param relation a relationship to check parameter and argument type
1906019018
* @param excludeArgument
1906119019
*/
19062-
function checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, isCtor: boolean, relation: Map<RelationComparisonResult>, reportErrors: boolean) {
19020+
function checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, relation: Map<RelationComparisonResult>, reportErrors: boolean) {
1906319021
// Stateless function components can have maximum of three arguments: "props", "context", and "updater".
1906419022
// However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props,
1906519023
// can be specified by users through attributes property.
19066-
const paramType = isCtor ? getJsxPropsTypeFromClassType(signature, isInJSFile(node), node) : getJsxPropsTypeFromCallSignature(signature, node);
19024+
const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node);
1906719025
const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined);
1906819026
return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes);
1906919027
}
@@ -19076,7 +19034,7 @@ namespace ts {
1907619034
excludeArgument: boolean[] | undefined,
1907719035
reportErrors: boolean) {
1907819036
if (isJsxOpeningLikeElement(node)) {
19079-
return checkApplicableSignatureForJsxOpeningLikeElement(node, signature, !isJsxStatelessFunctionReference(node), relation, reportErrors);
19037+
return checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, reportErrors);
1908019038
}
1908119039
const thisType = getThisTypeOfSignature(signature);
1908219040
if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) {
@@ -20000,33 +19958,63 @@ namespace ts {
2000019958
return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp, headMessage);
2000119959
}
2000219960

19961+
function createSignatureForJSXIntrinsic(node: JsxOpeningLikeElement, result: Type): Signature {
19962+
// TODO: Since this builds some nodes for the fake'd up signature, this might be worth caching on the node
19963+
const namespace = getJsxNamespaceAt(node);
19964+
const exports = namespace && getExportsOfSymbol(namespace);
19965+
// TODO: We fake up a SFC signature for each intrinsic, however a more specific per-element signature drawn from the JSX declaration
19966+
// file would probably be preferable.
19967+
const typeSymbol = exports && getSymbol(exports, JsxNames.Element, SymbolFlags.Type);
19968+
const returnNode = typeSymbol && nodeBuilder.symbolToEntityName(typeSymbol, SymbolFlags.Type, node);
19969+
const declaration = createFunctionTypeNode(/*typeParameters*/ undefined,
19970+
[createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotdotdot*/ undefined, "props", /*questionMark*/ undefined, nodeBuilder.typeToTypeNode(result, node))],
19971+
returnNode ? createTypeReferenceNode(returnNode, /*typeArguments*/ undefined) : createKeywordTypeNode(SyntaxKind.AnyKeyword)
19972+
);
19973+
const parameterSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "props" as __String);
19974+
parameterSymbol.type = result;
19975+
return createSignature(
19976+
declaration,
19977+
/*typeParameters*/ undefined,
19978+
/*thisParameter*/ undefined,
19979+
[parameterSymbol],
19980+
typeSymbol ? getDeclaredTypeOfSymbol(typeSymbol) : errorType,
19981+
/*returnTypePredicate*/ undefined,
19982+
1,
19983+
/*hasRestparameter*/ false,
19984+
/*hasLiteralTypes*/ false
19985+
);
19986+
}
19987+
2000319988
function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
2000419989
if (isJsxIntrinsicIdentifier(node.tagName)) {
2000519990
const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node);
20006-
checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(node.attributes, CheckMode.Normal), result, node.tagName, node.attributes);
20007-
return anySignature; // TODO: Fake up a signature from the intrinsic types
19991+
const fakeSignature = createSignatureForJSXIntrinsic(node, result);
19992+
checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined), result, node.tagName, node.attributes);
19993+
return fakeSignature;
2000819994
}
2000919995
const exprTypes = checkExpression(node.tagName);
2001019996
const apparentType = getApparentType(exprTypes);
2001119997
if (apparentType === errorType) {
2001219998
return resolveErrorCall(node);
2001319999
}
2001420000

20015-
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
20016-
const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
20017-
if (isUntypedFunctionCall(exprTypes, apparentType, callSignatures.length, constructSignatures.length) || exprTypes.flags & TypeFlags.String) {
20018-
return resolveUntypedCall(node);
20019-
}
20020-
2002120001
if (exprTypes.flags & TypeFlags.StringLiteral) {
2002220002
const intrinsicType = getIntrinsicAttributesTypeFromStringLiteralType(exprTypes as StringLiteralType, node);
2002320003
if (!intrinsicType) {
2002420004
error(node, Diagnostics.Property_0_does_not_exist_on_type_1, (exprTypes as StringLiteralType).value, "JSX." + JsxNames.IntrinsicElements);
20005+
return resolveUntypedCall(node);
2002520006
}
2002620007
else {
20027-
checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(node.attributes, CheckMode.Normal), intrinsicType, node.tagName, node.attributes);
20008+
const fakeSignature = createSignatureForJSXIntrinsic(node, intrinsicType);
20009+
checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined), intrinsicType, node.tagName, node.attributes);
20010+
return fakeSignature;
2002820011
}
20029-
return anySignature; // TODO: Fake up a signature from the intrinsic types
20012+
}
20013+
20014+
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
20015+
const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
20016+
if (exprTypes.flags & TypeFlags.String || isUntypedFunctionCall(exprTypes, apparentType, callSignatures.length, constructSignatures.length)) {
20017+
return resolveUntypedCall(node);
2003020018
}
2003120019

2003220020
const signatures = getUninstantiatedJsxSignaturesOfType(apparentType);

0 commit comments

Comments
 (0)