Skip to content

Commit 4b95ba7

Browse files
committed
Skip leading signature incompatability flattening
1 parent d4fcff2 commit 4b95ba7

15 files changed

+239
-110
lines changed

src/compiler/checker.ts

+48-22
Original file line numberDiff line numberDiff line change
@@ -12343,7 +12343,7 @@ namespace ts {
1234312343
ignoreReturnTypes: boolean,
1234412344
reportErrors: boolean,
1234512345
errorReporter: ErrorReporter | undefined,
12346-
incompatibleErrorReporter: (() => void) | undefined,
12346+
incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined,
1234712347
compareTypes: TypeComparer): Ternary {
1234812348
// TODO (drosen): De-duplicate code between related functions.
1234912349
if (source === target) {
@@ -12461,7 +12461,7 @@ namespace ts {
1246112461
result &= callbackCheck === CallbackCheck.Bivariant && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
1246212462
compareTypes(sourceReturnType, targetReturnType, reportErrors);
1246312463
if (!result && reportErrors && incompatibleErrorReporter) {
12464-
incompatibleErrorReporter();
12464+
incompatibleErrorReporter(sourceReturnType, targetReturnType);
1246512465
}
1246612466
}
1246712467

@@ -12766,6 +12766,7 @@ namespace ts {
1276612766
// The first error will be the innermost, while the last will be the outermost - so by popping off the end,
1276712767
// we can build from left to right
1276812768
let path = "";
12769+
const secondaryRootErrors: typeof incompatibleStack = [];
1276912770
while (stack.length) {
1277012771
const [msg, ...args] = stack.pop()!;
1277112772
switch (msg.code) {
@@ -12793,27 +12794,52 @@ namespace ts {
1279312794
}
1279412795
break;
1279512796
}
12796-
case Diagnostics.Call_signature_return_types_are_incompatible.code: {
12797-
path = path.length === 0 ? "(...)" : `${path}(...)`;
12798-
break;
12799-
}
12800-
case Diagnostics.Construct_signature_return_types_are_incompatible.code: {
12801-
path = path.length === 0 ? "new (...)" : `new ${path}(...)`;
12802-
break;
12803-
}
12804-
case Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types.code: {
12805-
path = path.length === 0 ? "()" : `${path}()`;
12806-
break;
12807-
}
12808-
case Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types.code: {
12809-
path = path.length === 0 ? "new ()" : `new ${path}()`;
12797+
case Diagnostics.Call_signature_return_types_0_and_1_are_incompatible.code:
12798+
case Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible.code:
12799+
case Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code:
12800+
case Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code: {
12801+
if (path.length === 0) {
12802+
// Don't flatten signature compatability errors at the start of a chain - instead prefer
12803+
// to unify (the with no arguments bit is excessive for printback) and print them back
12804+
let mappedMsg = msg;
12805+
if (msg.code === Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code) {
12806+
mappedMsg = Diagnostics.Call_signature_return_types_0_and_1_are_incompatible;
12807+
}
12808+
else if (msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code) {
12809+
mappedMsg = Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible;
12810+
}
12811+
secondaryRootErrors.unshift([mappedMsg, args[0], args[1]]);
12812+
}
12813+
else {
12814+
const prefix = (msg.code === Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible.code ||
12815+
msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code)
12816+
? "new "
12817+
: "";
12818+
const params = (msg.code === Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code ||
12819+
msg.code === Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1.code)
12820+
? ""
12821+
: "...";
12822+
path = `${prefix}${path}(${params})`;
12823+
}
1281012824
break;
1281112825
}
1281212826
default:
1281312827
return Debug.fail(`Unhandled Diagnostic: ${msg.code}`);
1281412828
}
1281512829
}
12816-
reportError(Diagnostics.The_types_of_0_are_incompatible_between_these_types, path);
12830+
if (path) {
12831+
reportError(Diagnostics.The_types_of_0_are_incompatible_between_these_types, path);
12832+
}
12833+
else {
12834+
// Remove the innermost secondary error as it will duplicate the error already reported by `reportRelationError` on entry
12835+
secondaryRootErrors.shift();
12836+
}
12837+
for (const [msg, ...args] of secondaryRootErrors) {
12838+
const originalValue = msg.elidedInCompatabilityPyramid;
12839+
msg.elidedInCompatabilityPyramid = false; // Teporarily override elision to ensure error is reported
12840+
reportError(msg, ...args);
12841+
msg.elidedInCompatabilityPyramid = originalValue;
12842+
}
1281712843
if (info) {
1281812844
// Actually do the last relation error
1281912845
reportRelationError(/*headMessage*/ undefined, ...info);
@@ -14304,22 +14330,22 @@ namespace ts {
1430414330

1430514331
function reportIncompatibleCallSignatureReturn(siga: Signature, sigb: Signature) {
1430614332
if (siga.parameters.length === 0 && sigb.parameters.length === 0) {
14307-
return () => reportIncompatibleError(Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types);
14333+
return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, typeToString(source), typeToString(target));
1430814334
}
14309-
return () => reportIncompatibleError(Diagnostics.Call_signature_return_types_are_incompatible);
14335+
return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Call_signature_return_types_0_and_1_are_incompatible, typeToString(source), typeToString(target));
1431014336
}
1431114337

1431214338
function reportIncompatibleConstructSignatureReturn(siga: Signature, sigb: Signature) {
1431314339
if (siga.parameters.length === 0 && sigb.parameters.length === 0) {
14314-
return () => reportIncompatibleError(Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types);
14340+
return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, typeToString(source), typeToString(target));
1431514341
}
14316-
return () => reportIncompatibleError(Diagnostics.Construct_signature_return_types_are_incompatible);
14342+
return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible, typeToString(source), typeToString(target));
1431714343
}
1431814344

1431914345
/**
1432014346
* See signatureAssignableTo, compareSignaturesIdentical
1432114347
*/
14322-
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: () => void): Ternary {
14348+
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
1432314349
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
1432414350
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, incompatibleReporter, isRelatedTo);
1432514351
}

src/compiler/diagnosticMessages.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1044,22 +1044,22 @@
10441044
"category": "Error",
10451045
"code": 2200
10461046
},
1047-
"Call signature return types are incompatible.": {
1047+
"Call signature return types '{0}' and '{1}' are incompatible.": {
10481048
"category": "Error",
10491049
"code": 2201,
10501050
"elidedInCompatabilityPyramid": true
10511051
},
1052-
"Construct signature return types are incompatible.": {
1052+
"Construct signature return types '{0}' and '{1}' are incompatible.": {
10531053
"category": "Error",
10541054
"code": 2202,
10551055
"elidedInCompatabilityPyramid": true
10561056
},
1057-
"Call signatures with no arguments have incompatible return types.": {
1057+
"Call signatures with no arguments have incompatible return types '{0}' and '{1}'.": {
10581058
"category": "Error",
10591059
"code": 2203,
10601060
"elidedInCompatabilityPyramid": true
10611061
},
1062-
"Construct signatures with no arguments have incompatible return types.": {
1062+
"Construct signatures with no arguments have incompatible return types '{0}' and '{1}'.": {
10631063
"category": "Error",
10641064
"code": 2204,
10651065
"elidedInCompatabilityPyramid": true

tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt

+6-4
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
55
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(8,23): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
66
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(9,23): error TS1055: Type 'PromiseLike' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
77
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(10,23): error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
8-
The types of '(new (...)).then(...)' are incompatible between these types.
9-
Type 'void' is not assignable to type 'PromiseLike<TResult1 | TResult2>'.
8+
Construct signature return types 'Thenable' and 'PromiseLike<T>' are incompatible.
9+
The types of 'then(...)' are incompatible between these types.
10+
Type 'void' is not assignable to type 'PromiseLike<TResult1 | TResult2>'.
1011
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(17,16): error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.
1112
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(23,25): error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.
1213

@@ -36,8 +37,9 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
3637
async function fn6(): Thenable { } // error
3738
~~~~~~~~
3839
!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.
39-
!!! error TS1055: The types of '(new (...)).then(...)' are incompatible between these types.
40-
!!! error TS1055: Type 'void' is not assignable to type 'PromiseLike<TResult1 | TResult2>'.
40+
!!! error TS1055: Construct signature return types 'Thenable' and 'PromiseLike<T>' are incompatible.
41+
!!! error TS1055: The types of 'then(...)' are incompatible between these types.
42+
!!! error TS1055: Type 'void' is not assignable to type 'PromiseLike<TResult1 | TResult2>'.
4143
async function fn7() { return; } // valid: Promise<void>
4244
async function fn8() { return 1; } // valid: Promise<number>
4345
async function fn9() { return null; } // valid: Promise<any>

tests/baselines/reference/generatorTypeCheck25.errors.txt

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck25.ts(4,5): error TS2322: Type '() => Generator<Bar | Baz, void, undefined>' is not assignable to type '() => Iterable<Foo>'.
2-
The types of '()[Symbol.iterator]().next(...)' are incompatible between these types.
3-
Type 'IteratorResult<Bar | Baz, void>' is not assignable to type 'IteratorResult<Foo, any>'.
4-
Type 'IteratorYieldResult<Bar | Baz>' is not assignable to type 'IteratorResult<Foo, any>'.
5-
Type 'IteratorYieldResult<Bar | Baz>' is not assignable to type 'IteratorYieldResult<Foo>'.
6-
Type 'Bar | Baz' is not assignable to type 'Foo'.
7-
Property 'x' is missing in type 'Baz' but required in type 'Foo'.
2+
Call signature return types 'Generator<Bar | Baz, void, undefined>' and 'Iterable<Foo>' are incompatible.
3+
The types of '[Symbol.iterator]().next(...)' are incompatible between these types.
4+
Type 'IteratorResult<Bar | Baz, void>' is not assignable to type 'IteratorResult<Foo, any>'.
5+
Type 'IteratorYieldResult<Bar | Baz>' is not assignable to type 'IteratorResult<Foo, any>'.
6+
Type 'IteratorYieldResult<Bar | Baz>' is not assignable to type 'IteratorYieldResult<Foo>'.
7+
Type 'Bar | Baz' is not assignable to type 'Foo'.
8+
Property 'x' is missing in type 'Baz' but required in type 'Foo'.
89

910

1011
==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck25.ts (1 errors) ====
@@ -14,12 +15,13 @@ tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck25.ts(4,5): error
1415
var g3: () => Iterable<Foo> = function* () {
1516
~~
1617
!!! error TS2322: Type '() => Generator<Bar | Baz, void, undefined>' is not assignable to type '() => Iterable<Foo>'.
17-
!!! error TS2322: The types of '()[Symbol.iterator]().next(...)' are incompatible between these types.
18-
!!! error TS2322: Type 'IteratorResult<Bar | Baz, void>' is not assignable to type 'IteratorResult<Foo, any>'.
19-
!!! error TS2322: Type 'IteratorYieldResult<Bar | Baz>' is not assignable to type 'IteratorResult<Foo, any>'.
20-
!!! error TS2322: Type 'IteratorYieldResult<Bar | Baz>' is not assignable to type 'IteratorYieldResult<Foo>'.
21-
!!! error TS2322: Type 'Bar | Baz' is not assignable to type 'Foo'.
22-
!!! error TS2322: Property 'x' is missing in type 'Baz' but required in type 'Foo'.
18+
!!! error TS2322: Call signature return types 'Generator<Bar | Baz, void, undefined>' and 'Iterable<Foo>' are incompatible.
19+
!!! error TS2322: The types of '[Symbol.iterator]().next(...)' are incompatible between these types.
20+
!!! error TS2322: Type 'IteratorResult<Bar | Baz, void>' is not assignable to type 'IteratorResult<Foo, any>'.
21+
!!! error TS2322: Type 'IteratorYieldResult<Bar | Baz>' is not assignable to type 'IteratorResult<Foo, any>'.
22+
!!! error TS2322: Type 'IteratorYieldResult<Bar | Baz>' is not assignable to type 'IteratorYieldResult<Foo>'.
23+
!!! error TS2322: Type 'Bar | Baz' is not assignable to type 'Foo'.
24+
!!! error TS2322: Property 'x' is missing in type 'Baz' but required in type 'Foo'.
2325
!!! related TS2728 tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck25.ts:1:13: 'x' is declared here.
2426
yield;
2527
yield new Bar;

tests/baselines/reference/generatorTypeCheck63.errors.txt

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck63.ts(24,61): error TS2345: Argument of type '(state: State) => Generator<number, State, undefined>' is not assignable to parameter of type '(a: State) => IterableIterator<State>'.
2-
The types of '(...).next(...)' are incompatible between these types.
3-
Type 'IteratorResult<number, State>' is not assignable to type 'IteratorResult<State, any>'.
4-
Type 'IteratorYieldResult<number>' is not assignable to type 'IteratorResult<State, any>'.
5-
Type 'IteratorYieldResult<number>' is not assignable to type 'IteratorYieldResult<State>'.
6-
Type 'number' is not assignable to type 'State'.
2+
Call signature return types 'Generator<number, State, undefined>' and 'IterableIterator<State>' are incompatible.
3+
The types of 'next(...)' are incompatible between these types.
4+
Type 'IteratorResult<number, State>' is not assignable to type 'IteratorResult<State, any>'.
5+
Type 'IteratorYieldResult<number>' is not assignable to type 'IteratorResult<State, any>'.
6+
Type 'IteratorYieldResult<number>' is not assignable to type 'IteratorYieldResult<State>'.
7+
Type 'number' is not assignable to type 'State'.
78

89

910
==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck63.ts (1 errors) ====
@@ -33,11 +34,12 @@ tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck63.ts(24,61): err
3334
export const Nothing: Strategy<State> = strategy("Nothing", function* (state: State) {
3435
~~~~~~~~
3536
!!! error TS2345: Argument of type '(state: State) => Generator<number, State, undefined>' is not assignable to parameter of type '(a: State) => IterableIterator<State>'.
36-
!!! error TS2345: The types of '(...).next(...)' are incompatible between these types.
37-
!!! error TS2345: Type 'IteratorResult<number, State>' is not assignable to type 'IteratorResult<State, any>'.
38-
!!! error TS2345: Type 'IteratorYieldResult<number>' is not assignable to type 'IteratorResult<State, any>'.
39-
!!! error TS2345: Type 'IteratorYieldResult<number>' is not assignable to type 'IteratorYieldResult<State>'.
40-
!!! error TS2345: Type 'number' is not assignable to type 'State'.
37+
!!! error TS2345: Call signature return types 'Generator<number, State, undefined>' and 'IterableIterator<State>' are incompatible.
38+
!!! error TS2345: The types of 'next(...)' are incompatible between these types.
39+
!!! error TS2345: Type 'IteratorResult<number, State>' is not assignable to type 'IteratorResult<State, any>'.
40+
!!! error TS2345: Type 'IteratorYieldResult<number>' is not assignable to type 'IteratorResult<State, any>'.
41+
!!! error TS2345: Type 'IteratorYieldResult<number>' is not assignable to type 'IteratorYieldResult<State>'.
42+
!!! error TS2345: Type 'number' is not assignable to type 'State'.
4143
yield 1;
4244
return state;
4345
});

0 commit comments

Comments
 (0)