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:
Wesley Wigham
2018-10-26 16:01:32 -07:00
committed by GitHub
parent 36dfd775b3
commit 972c403cd8
8 changed files with 169 additions and 49 deletions

View File

@@ -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));

View File

@@ -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,