mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-18 06:17:19 -05:00
JSX uses mixed signatures and union sigs use subtype on partial match (#28141)
* JSX uses mixed signatures and union sigs use subtype on partial match * Small improvement
This commit is contained in:
@@ -6567,7 +6567,7 @@ namespace ts {
|
||||
|
||||
function findMatchingSignature(signatureList: ReadonlyArray<Signature>, signature: Signature, partialMatch: boolean, ignoreThisTypes: boolean, ignoreReturnTypes: boolean): Signature | undefined {
|
||||
for (const s of signatureList) {
|
||||
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, compareTypesIdentical)) {
|
||||
if (compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, partialMatch ? compareTypesSubtypeOf : compareTypesIdentical)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -6603,8 +6603,7 @@ namespace ts {
|
||||
// Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional
|
||||
// parameters and may differ in return types. When signatures differ in return types, the resulting return
|
||||
// type is the union of the constituent return types.
|
||||
function getUnionSignatures(types: ReadonlyArray<Type>, kind: SignatureKind): Signature[] {
|
||||
const signatureLists = map(types, t => getSignaturesOfType(t, kind));
|
||||
function getUnionSignatures(signatureLists: ReadonlyArray<ReadonlyArray<Signature>>): Signature[] {
|
||||
let result: Signature[] | undefined;
|
||||
for (let i = 0; i < signatureLists.length; i++) {
|
||||
for (const signature of signatureLists[i]) {
|
||||
@@ -6650,8 +6649,8 @@ namespace ts {
|
||||
function resolveUnionTypeMembers(type: UnionType) {
|
||||
// The members and properties collections are empty for union types. To get all properties of a union
|
||||
// type use getPropertiesOfType (only the language service uses this).
|
||||
const callSignatures = getUnionSignatures(type.types, SignatureKind.Call);
|
||||
const constructSignatures = getUnionSignatures(type.types, SignatureKind.Construct);
|
||||
const callSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Call)));
|
||||
const constructSignatures = getUnionSignatures(map(type.types, t => getSignaturesOfType(t, SignatureKind.Construct)));
|
||||
const stringIndexInfo = getUnionIndexInfo(type.types, IndexKind.String);
|
||||
const numberIndexInfo = getUnionIndexInfo(type.types, IndexKind.Number);
|
||||
setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
|
||||
@@ -10691,6 +10690,10 @@ namespace ts {
|
||||
return isTypeRelatedTo(source, target, assignableRelation) ? Ternary.True : Ternary.False;
|
||||
}
|
||||
|
||||
function compareTypesSubtypeOf(source: Type, target: Type): Ternary {
|
||||
return isTypeRelatedTo(source, target, subtypeRelation) ? Ternary.True : Ternary.False;
|
||||
}
|
||||
|
||||
function isTypeSubtypeOf(source: Type, target: Type): boolean {
|
||||
return isTypeRelatedTo(source, target, subtypeRelation);
|
||||
}
|
||||
@@ -12880,7 +12883,7 @@ namespace ts {
|
||||
for (let i = 0; i < targetLen; i++) {
|
||||
const s = getTypeAtPosition(source, i);
|
||||
const t = getTypeAtPosition(target, i);
|
||||
const related = compareTypes(s, t);
|
||||
const related = compareTypes(t, s);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
@@ -17108,7 +17111,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getEffectiveFirstArgumentForJsxSignature(signature: Signature, node: JsxOpeningLikeElement) {
|
||||
return isJsxStatelessFunctionReference(node) ? getJsxPropsTypeFromCallSignature(signature, node) : getJsxPropsTypeFromClassType(signature, node);
|
||||
return getJsxReferenceKind(node) !== JsxReferenceKind.Component ? getJsxPropsTypeFromCallSignature(signature, node) : getJsxPropsTypeFromClassType(signature, node);
|
||||
}
|
||||
|
||||
function getJsxPropsTypeFromCallSignature(sig: Signature, context: JsxOpeningLikeElement) {
|
||||
@@ -18004,13 +18007,17 @@ namespace ts {
|
||||
return getNameFromJsxElementAttributesContainer(JsxNames.ElementChildrenAttributeNameContainer, jsxNamespace);
|
||||
}
|
||||
|
||||
function getUninstantiatedJsxSignaturesOfType(elementType: Type) {
|
||||
function getUninstantiatedJsxSignaturesOfType(elementType: Type): ReadonlyArray<Signature> {
|
||||
// Resolve the signatures, preferring constructor
|
||||
let signatures = getSignaturesOfType(elementType, SignatureKind.Construct);
|
||||
if (signatures.length === 0) {
|
||||
// No construct signatures, try call signatures
|
||||
signatures = getSignaturesOfType(elementType, SignatureKind.Call);
|
||||
}
|
||||
if (signatures.length === 0 && elementType.flags & TypeFlags.Union) {
|
||||
// If each member has some combination of new/call signatures; make a union signature list for those
|
||||
signatures = getUnionSignatures(map((elementType as UnionType).types, getUninstantiatedJsxSignaturesOfType));
|
||||
}
|
||||
return signatures;
|
||||
}
|
||||
|
||||
@@ -18036,20 +18043,29 @@ namespace ts {
|
||||
return anyType;
|
||||
}
|
||||
|
||||
function checkJsxReturnAssignableToAppropriateBound(isSFC: boolean, elemInstanceType: Type, openingLikeElement: Node) {
|
||||
if (isSFC) {
|
||||
function checkJsxReturnAssignableToAppropriateBound(refKind: JsxReferenceKind, elemInstanceType: Type, openingLikeElement: Node) {
|
||||
if (refKind === JsxReferenceKind.Function) {
|
||||
const sfcReturnConstraint = getJsxStatelessElementTypeAt(openingLikeElement);
|
||||
if (sfcReturnConstraint) {
|
||||
checkTypeRelatedTo(elemInstanceType, sfcReturnConstraint, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (refKind === JsxReferenceKind.Component) {
|
||||
const classConstraint = getJsxElementClassTypeAt(openingLikeElement);
|
||||
if (classConstraint) {
|
||||
// Issue an error if this return type isn't assignable to JSX.ElementClass or JSX.Element, failing that
|
||||
checkTypeRelatedTo(elemInstanceType, classConstraint, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements);
|
||||
}
|
||||
}
|
||||
else { // Mixed
|
||||
const sfcReturnConstraint = getJsxStatelessElementTypeAt(openingLikeElement);
|
||||
const classConstraint = getJsxElementClassTypeAt(openingLikeElement);
|
||||
if (!sfcReturnConstraint || !classConstraint) {
|
||||
return;
|
||||
}
|
||||
const combined = getUnionType([sfcReturnConstraint, classConstraint]);
|
||||
checkTypeRelatedTo(elemInstanceType, combined, assignableRelation, openingLikeElement, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -18139,7 +18155,7 @@ namespace ts {
|
||||
|
||||
if (isNodeOpeningLikeElement) {
|
||||
const sig = getResolvedSignature(node as JsxOpeningLikeElement);
|
||||
checkJsxReturnAssignableToAppropriateBound(isJsxStatelessFunctionReference(node as JsxOpeningLikeElement), getReturnTypeOfSignature(sig), node);
|
||||
checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(node as JsxOpeningLikeElement), getReturnTypeOfSignature(sig), node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19174,12 +19190,18 @@ namespace ts {
|
||||
return typeArgumentTypes;
|
||||
}
|
||||
|
||||
function isJsxStatelessFunctionReference(node: JsxOpeningLikeElement) {
|
||||
function getJsxReferenceKind(node: JsxOpeningLikeElement): JsxReferenceKind {
|
||||
if (isJsxIntrinsicIdentifier(node.tagName)) {
|
||||
return true;
|
||||
return JsxReferenceKind.Mixed;
|
||||
}
|
||||
const tagType = checkExpression(node.tagName);
|
||||
return !length(getSignaturesOfType(getApparentType(tagType), SignatureKind.Construct));
|
||||
const tagType = getApparentType(checkExpression(node.tagName));
|
||||
if (length(getSignaturesOfType(tagType, SignatureKind.Construct))) {
|
||||
return JsxReferenceKind.Component;
|
||||
}
|
||||
if (length(getSignaturesOfType(tagType, SignatureKind.Call))) {
|
||||
return JsxReferenceKind.Function;
|
||||
}
|
||||
return JsxReferenceKind.Mixed;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20182,13 +20204,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
|
||||
const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
|
||||
if (exprTypes.flags & TypeFlags.String || isUntypedFunctionCall(exprTypes, apparentType, callSignatures.length, constructSignatures.length)) {
|
||||
const signatures = getUninstantiatedJsxSignaturesOfType(apparentType);
|
||||
if (exprTypes.flags & TypeFlags.String || isUntypedFunctionCall(exprTypes, apparentType, signatures.length, /*constructSignatures*/ 0)) {
|
||||
return resolveUntypedCall(node);
|
||||
}
|
||||
|
||||
const signatures = getUninstantiatedJsxSignaturesOfType(apparentType);
|
||||
if (signatures.length === 0) {
|
||||
// We found no signatures at all, which is an error
|
||||
error(node.tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(node.tagName));
|
||||
|
||||
@@ -4217,6 +4217,13 @@ namespace ts {
|
||||
substitute: Type; // Type to substitute for type parameter
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum JsxReferenceKind {
|
||||
Component,
|
||||
Function,
|
||||
Mixed
|
||||
}
|
||||
|
||||
export const enum SignatureKind {
|
||||
Call,
|
||||
Construct,
|
||||
|
||||
Reference in New Issue
Block a user