mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 10:46:28 -05:00
Introduce flattened error reporting for properties, call signatures, and construct signatures (#33473)
* Introduce flattened error reporting for properties, call signatures, and construct signatures * Update message, specialize output for argument-less signatures * Skip leading signature incompatability flattening * Add return type specialized message
This commit is contained in:
@@ -12329,7 +12329,7 @@ namespace ts {
|
||||
target: Signature,
|
||||
ignoreReturnTypes: boolean): boolean {
|
||||
return compareSignaturesRelated(source, target, CallbackCheck.None, ignoreReturnTypes, /*reportErrors*/ false,
|
||||
/*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
|
||||
/*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
|
||||
}
|
||||
|
||||
type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
|
||||
@@ -12352,6 +12352,7 @@ namespace ts {
|
||||
ignoreReturnTypes: boolean,
|
||||
reportErrors: boolean,
|
||||
errorReporter: ErrorReporter | undefined,
|
||||
incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined,
|
||||
compareTypes: TypeComparer): Ternary {
|
||||
// TODO (drosen): De-duplicate code between related functions.
|
||||
if (source === target) {
|
||||
@@ -12422,7 +12423,7 @@ namespace ts {
|
||||
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
|
||||
const related = callbacks ?
|
||||
// TODO: GH#18217 It will work if they're both `undefined`, but not if only one is
|
||||
compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) :
|
||||
compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, incompatibleErrorReporter, compareTypes) :
|
||||
!callbackCheck && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
@@ -12468,6 +12469,9 @@ namespace ts {
|
||||
// wouldn't be co-variant for T without this rule.
|
||||
result &= callbackCheck === CallbackCheck.Bivariant && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
|
||||
compareTypes(sourceReturnType, targetReturnType, reportErrors);
|
||||
if (!result && reportErrors && incompatibleErrorReporter) {
|
||||
incompatibleErrorReporter(sourceReturnType, targetReturnType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12677,11 +12681,16 @@ namespace ts {
|
||||
let depth = 0;
|
||||
let expandingFlags = ExpandingFlags.None;
|
||||
let overflow = false;
|
||||
let overrideNextErrorInfo: DiagnosticMessageChain | undefined;
|
||||
let overrideNextErrorInfo = 0; // How many `reportRelationError` calls should be skipped in the elaboration pyramid
|
||||
let lastSkippedInfo: [Type, Type] | undefined;
|
||||
let incompatibleStack: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] = [];
|
||||
|
||||
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
|
||||
|
||||
const result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage);
|
||||
if (incompatibleStack.length) {
|
||||
reportIncompatibleStack();
|
||||
}
|
||||
if (overflow) {
|
||||
const diag = error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target));
|
||||
if (errorOutputContainer) {
|
||||
@@ -12726,8 +12735,134 @@ namespace ts {
|
||||
}
|
||||
return result !== Ternary.False;
|
||||
|
||||
function resetErrorInfo(saved: ReturnType<typeof captureErrorCalculationState>) {
|
||||
errorInfo = saved.errorInfo;
|
||||
lastSkippedInfo = saved.lastSkippedInfo;
|
||||
incompatibleStack = saved.incompatibleStack;
|
||||
overrideNextErrorInfo = saved.overrideNextErrorInfo;
|
||||
relatedInfo = saved.relatedInfo;
|
||||
}
|
||||
|
||||
function captureErrorCalculationState() {
|
||||
return {
|
||||
errorInfo,
|
||||
lastSkippedInfo,
|
||||
incompatibleStack: incompatibleStack.slice(),
|
||||
overrideNextErrorInfo,
|
||||
relatedInfo: !relatedInfo ? undefined : relatedInfo.slice() as ([DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined)
|
||||
};
|
||||
}
|
||||
|
||||
function reportIncompatibleError(message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number) {
|
||||
overrideNextErrorInfo++; // Suppress the next relation error
|
||||
lastSkippedInfo = undefined; // Reset skipped info cache
|
||||
incompatibleStack.push([message, arg0, arg1, arg2, arg3]);
|
||||
}
|
||||
|
||||
function reportIncompatibleStack() {
|
||||
const stack = incompatibleStack;
|
||||
incompatibleStack = [];
|
||||
const info = lastSkippedInfo;
|
||||
lastSkippedInfo = undefined;
|
||||
if (stack.length === 1) {
|
||||
reportError(...stack[0]);
|
||||
if (info) {
|
||||
// Actually do the last relation error
|
||||
reportRelationError(/*headMessage*/ undefined, ...info);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// The first error will be the innermost, while the last will be the outermost - so by popping off the end,
|
||||
// we can build from left to right
|
||||
let path = "";
|
||||
const secondaryRootErrors: typeof incompatibleStack = [];
|
||||
while (stack.length) {
|
||||
const [msg, ...args] = stack.pop()!;
|
||||
switch (msg.code) {
|
||||
case Diagnostics.Types_of_property_0_are_incompatible.code: {
|
||||
// Parenthesize a `new` if there is one
|
||||
if (path.indexOf("new ") === 0) {
|
||||
path = `(${path})`;
|
||||
}
|
||||
const str = "" + args[0];
|
||||
// If leading, just print back the arg (irrespective of if it's a valid identifier)
|
||||
if (path.length === 0) {
|
||||
path = `${str}`;
|
||||
}
|
||||
// Otherwise write a dotted name if possible
|
||||
else if (isIdentifierText(str, compilerOptions.target)) {
|
||||
path = `${path}.${str}`;
|
||||
}
|
||||
// Failing that, check if the name is already a computed name
|
||||
else if (str[0] === "[" && str[str.length - 1] === "]") {
|
||||
path = `${path}${str}`;
|
||||
}
|
||||
// And finally write out a computed name as a last resort
|
||||
else {
|
||||
path = `${path}[${str}]`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Diagnostics.Call_signature_return_types_0_and_1_are_incompatible.code:
|
||||
case Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible.code:
|
||||
case Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code:
|
||||
case Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code: {
|
||||
if (path.length === 0) {
|
||||
// Don't flatten signature compatability errors at the start of a chain - instead prefer
|
||||
// to unify (the with no arguments bit is excessive for printback) and print them back
|
||||
let mappedMsg = msg;
|
||||
if (msg.code === Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code) {
|
||||
mappedMsg = Diagnostics.Call_signature_return_types_0_and_1_are_incompatible;
|
||||
}
|
||||
else if (msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code) {
|
||||
mappedMsg = Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible;
|
||||
}
|
||||
secondaryRootErrors.unshift([mappedMsg, args[0], args[1]]);
|
||||
}
|
||||
else {
|
||||
const prefix = (msg.code === Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible.code ||
|
||||
msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code)
|
||||
? "new "
|
||||
: "";
|
||||
const params = (msg.code === Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code ||
|
||||
msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code)
|
||||
? ""
|
||||
: "...";
|
||||
path = `${prefix}${path}(${params})`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return Debug.fail(`Unhandled Diagnostic: ${msg.code}`);
|
||||
}
|
||||
}
|
||||
if (path) {
|
||||
reportError(path[path.length - 1] === ")"
|
||||
? Diagnostics.The_types_returned_by_0_are_incompatible_between_these_types
|
||||
: Diagnostics.The_types_of_0_are_incompatible_between_these_types,
|
||||
path
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Remove the innermost secondary error as it will duplicate the error already reported by `reportRelationError` on entry
|
||||
secondaryRootErrors.shift();
|
||||
}
|
||||
for (const [msg, ...args] of secondaryRootErrors) {
|
||||
const originalValue = msg.elidedInCompatabilityPyramid;
|
||||
msg.elidedInCompatabilityPyramid = false; // Teporarily override elision to ensure error is reported
|
||||
reportError(msg, ...args);
|
||||
msg.elidedInCompatabilityPyramid = originalValue;
|
||||
}
|
||||
if (info) {
|
||||
// Actually do the last relation error
|
||||
reportRelationError(/*headMessage*/ undefined, ...info);
|
||||
}
|
||||
}
|
||||
|
||||
function reportError(message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void {
|
||||
Debug.assert(!!errorNode);
|
||||
if (incompatibleStack.length) reportIncompatibleStack();
|
||||
if (message.elidedInCompatabilityPyramid) return;
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
@@ -12742,6 +12877,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function reportRelationError(message: DiagnosticMessage | undefined, source: Type, target: Type) {
|
||||
if (incompatibleStack.length) reportIncompatibleStack();
|
||||
const [sourceType, targetType] = getTypeNamesForErrorDisplay(source, target);
|
||||
|
||||
if (target.flags & TypeFlags.TypeParameter && target.immediateBaseConstraint !== undefined && isTypeAssignableTo(source, target.immediateBaseConstraint)) {
|
||||
@@ -12899,7 +13035,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
let result = Ternary.False;
|
||||
const saveErrorInfo = errorInfo;
|
||||
const saveErrorInfo = captureErrorCalculationState();
|
||||
let isIntersectionConstituent = !!isApparentIntersectionConstituent;
|
||||
|
||||
// Note that these checks are specifically ordered to produce correct results. In particular,
|
||||
@@ -12949,7 +13085,7 @@ namespace ts {
|
||||
}
|
||||
if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) {
|
||||
if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12966,19 +13102,21 @@ namespace ts {
|
||||
const constraint = getUnionConstraintOfIntersection(<IntersectionType>source, !!(target.flags & TypeFlags.Union));
|
||||
if (constraint) {
|
||||
if (result = isRelatedTo(constraint, target, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result && reportErrors) {
|
||||
let maybeSuppress = overrideNextErrorInfo;
|
||||
overrideNextErrorInfo = undefined;
|
||||
let maybeSuppress = overrideNextErrorInfo > 0;
|
||||
if (maybeSuppress) {
|
||||
overrideNextErrorInfo--;
|
||||
}
|
||||
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
|
||||
const currentError = errorInfo;
|
||||
tryElaborateArrayLikeErrors(source, target, reportErrors);
|
||||
if (errorInfo !== currentError) {
|
||||
maybeSuppress = errorInfo;
|
||||
maybeSuppress = !!errorInfo;
|
||||
}
|
||||
}
|
||||
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
|
||||
@@ -12998,6 +13136,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
if (!headMessage && maybeSuppress) {
|
||||
lastSkippedInfo = [source, target];
|
||||
// Used by, eg, missing property checking to replace the top-level message with a more informative one
|
||||
return result;
|
||||
}
|
||||
@@ -13439,7 +13578,7 @@ namespace ts {
|
||||
let result: Ternary;
|
||||
let originalErrorInfo: DiagnosticMessageChain | undefined;
|
||||
let varianceCheckFailed = false;
|
||||
const saveErrorInfo = errorInfo;
|
||||
const saveErrorInfo = captureErrorCalculationState();
|
||||
|
||||
// We limit alias variance probing to only object and conditional types since their alias behavior
|
||||
// is more predictable than other, interned types, which may or may not have an alias depending on
|
||||
@@ -13531,7 +13670,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
originalErrorInfo = errorInfo;
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13543,7 +13682,7 @@ namespace ts {
|
||||
result &= isRelatedTo((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType, reportErrors);
|
||||
}
|
||||
if (result) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -13552,25 +13691,25 @@ namespace ts {
|
||||
if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
|
||||
// A type variable with no constraint is not related to the non-primitive object type.
|
||||
if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed
|
||||
else if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, isIntersectionConstituent)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
// slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example
|
||||
else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, reportErrors, /*headMessage*/ undefined, isIntersectionConstituent)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.Index) {
|
||||
if (result = isRelatedTo(keyofConstraintType, target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -13595,7 +13734,7 @@ namespace ts {
|
||||
result &= isRelatedTo(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target), reportErrors);
|
||||
}
|
||||
if (result) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -13604,14 +13743,14 @@ namespace ts {
|
||||
const distributiveConstraint = getConstraintOfDistributiveConditionalType(<ConditionalType>source);
|
||||
if (distributiveConstraint) {
|
||||
if (result = isRelatedTo(distributiveConstraint, target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
const defaultConstraint = getDefaultConstraintOfConditionalType(<ConditionalType>source);
|
||||
if (defaultConstraint) {
|
||||
if (result = isRelatedTo(defaultConstraint, target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -13625,7 +13764,7 @@ namespace ts {
|
||||
if (isGenericMappedType(target)) {
|
||||
if (isGenericMappedType(source)) {
|
||||
if (result = mappedTypeRelatedTo(source, target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -13671,7 +13810,7 @@ namespace ts {
|
||||
// relates to X. Thus, we include intersection types on the source side here.
|
||||
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
|
||||
// Report structural errors only if we haven't reported any errors yet
|
||||
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !sourceIsPrimitive;
|
||||
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo.errorInfo && !sourceIsPrimitive;
|
||||
result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, isIntersectionConstituent);
|
||||
if (result) {
|
||||
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors);
|
||||
@@ -13686,7 +13825,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
if (varianceCheckFailed && result) {
|
||||
errorInfo = originalErrorInfo || errorInfo || saveErrorInfo; // Use variance error (there is no structural one) and return false
|
||||
errorInfo = originalErrorInfo || errorInfo || saveErrorInfo.errorInfo; // Use variance error (there is no structural one) and return false
|
||||
}
|
||||
else if (result) {
|
||||
return result;
|
||||
@@ -13718,7 +13857,7 @@ namespace ts {
|
||||
// We elide the variance-based error elaborations, since those might not be too helpful, since we'll potentially
|
||||
// be assuming identity of the type parameter.
|
||||
originalErrorInfo = undefined;
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return undefined;
|
||||
}
|
||||
const allowStructuralFallback = targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances);
|
||||
@@ -13746,7 +13885,7 @@ namespace ts {
|
||||
// comparison unexpectedly succeeds. This can happen when the structural comparison result
|
||||
// is a Ternary.Maybe for example caused by the recursion depth limiter.
|
||||
originalErrorInfo = errorInfo;
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13980,7 +14119,7 @@ namespace ts {
|
||||
const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, isIntersectionConstituent);
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp));
|
||||
reportIncompatibleError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp));
|
||||
}
|
||||
return Ternary.False;
|
||||
}
|
||||
@@ -14022,8 +14161,8 @@ namespace ts {
|
||||
if (length(unmatchedProperty.declarations)) {
|
||||
associateRelatedInfo(createDiagnosticForNode(unmatchedProperty.declarations[0], Diagnostics._0_is_declared_here, propName));
|
||||
}
|
||||
if (shouldSkipElaboration) {
|
||||
overrideNextErrorInfo = errorInfo;
|
||||
if (shouldSkipElaboration && errorInfo) {
|
||||
overrideNextErrorInfo++;
|
||||
}
|
||||
}
|
||||
else if (tryElaborateArrayLikeErrors(source, target, /*reportErrors*/ false)) {
|
||||
@@ -14033,8 +14172,8 @@ namespace ts {
|
||||
else {
|
||||
reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2, typeToString(source), typeToString(target), map(props, p => symbolToString(p)).join(", "));
|
||||
}
|
||||
if (shouldSkipElaboration) {
|
||||
overrideNextErrorInfo = errorInfo;
|
||||
if (shouldSkipElaboration && errorInfo) {
|
||||
overrideNextErrorInfo++;
|
||||
}
|
||||
}
|
||||
// ELSE: No array like or unmatched property error - just issue top level error (errorInfo = undefined)
|
||||
@@ -14157,7 +14296,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
let result = Ternary.True;
|
||||
const saveErrorInfo = errorInfo;
|
||||
const saveErrorInfo = captureErrorCalculationState();
|
||||
const incompatibleReporter = kind === SignatureKind.Construct ? reportIncompatibleConstructSignatureReturn : reportIncompatibleCallSignatureReturn;
|
||||
|
||||
if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) {
|
||||
// We have instantiations of the same anonymous type (which typically will be the type of a
|
||||
@@ -14165,7 +14305,7 @@ namespace ts {
|
||||
// of the much more expensive N * M comparison matrix we explore below. We erase type parameters
|
||||
// as they are known to always be the same.
|
||||
for (let i = 0; i < targetSignatures.length; i++) {
|
||||
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors);
|
||||
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors, incompatibleReporter(sourceSignatures[i], targetSignatures[i]));
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
@@ -14179,17 +14319,17 @@ namespace ts {
|
||||
// this regardless of the number of signatures, but the potential costs are prohibitive due
|
||||
// to the quadratic nature of the logic below.
|
||||
const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks;
|
||||
result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors);
|
||||
result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors, incompatibleReporter(sourceSignatures[0], targetSignatures[0]));
|
||||
}
|
||||
else {
|
||||
outer: for (const t of targetSignatures) {
|
||||
// Only elaborate errors from the first failure
|
||||
let shouldElaborateErrors = reportErrors;
|
||||
for (const s of sourceSignatures) {
|
||||
const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors);
|
||||
const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors, incompatibleReporter(s, t));
|
||||
if (related) {
|
||||
result &= related;
|
||||
errorInfo = saveErrorInfo;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
continue outer;
|
||||
}
|
||||
shouldElaborateErrors = false;
|
||||
@@ -14206,12 +14346,26 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function reportIncompatibleCallSignatureReturn(siga: Signature, sigb: Signature) {
|
||||
if (siga.parameters.length === 0 && sigb.parameters.length === 0) {
|
||||
return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, typeToString(source), typeToString(target));
|
||||
}
|
||||
return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Call_signature_return_types_0_and_1_are_incompatible, typeToString(source), typeToString(target));
|
||||
}
|
||||
|
||||
function reportIncompatibleConstructSignatureReturn(siga: Signature, sigb: Signature) {
|
||||
if (siga.parameters.length === 0 && sigb.parameters.length === 0) {
|
||||
return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, typeToString(source), typeToString(target));
|
||||
}
|
||||
return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible, typeToString(source), typeToString(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* See signatureAssignableTo, compareSignaturesIdentical
|
||||
*/
|
||||
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean): Ternary {
|
||||
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
|
||||
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
|
||||
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
|
||||
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, incompatibleReporter, isRelatedTo);
|
||||
}
|
||||
|
||||
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
|
||||
|
||||
@@ -1040,6 +1040,35 @@
|
||||
"code": 1357
|
||||
},
|
||||
|
||||
"The types of '{0}' are incompatible between these types.": {
|
||||
"category": "Error",
|
||||
"code": 2200
|
||||
},
|
||||
"The types returned by '{0}' are incompatible between these types.": {
|
||||
"category": "Error",
|
||||
"code": 2201
|
||||
},
|
||||
"Call signature return types '{0}' and '{1}' are incompatible.": {
|
||||
"category": "Error",
|
||||
"code": 2202,
|
||||
"elidedInCompatabilityPyramid": true
|
||||
},
|
||||
"Construct signature return types '{0}' and '{1}' are incompatible.": {
|
||||
"category": "Error",
|
||||
"code": 2203,
|
||||
"elidedInCompatabilityPyramid": true
|
||||
},
|
||||
"Call signatures with no arguments have incompatible return types '{0}' and '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2204,
|
||||
"elidedInCompatabilityPyramid": true
|
||||
},
|
||||
"Construct signatures with no arguments have incompatible return types '{0}' and '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2205,
|
||||
"elidedInCompatabilityPyramid": true
|
||||
},
|
||||
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 2300
|
||||
|
||||
@@ -4631,6 +4631,8 @@ namespace ts {
|
||||
code: number;
|
||||
message: string;
|
||||
reportsUnnecessary?: {};
|
||||
/* @internal */
|
||||
elidedInCompatabilityPyramid?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user