diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3273b7cb18501..463d00c13e267 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8971,9 +8971,8 @@ namespace ts { return undefined; } - function getConstraintDeclaration(type: TypeParameter) { - const decl = type.symbol && getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter); - return decl && getEffectiveConstraintOfTypeParameter(decl); + function getConstraintDeclaration(type: TypeParameter): TypeNode | undefined { + return mapDefined(filter(type.symbol && type.symbol.declarations, isTypeParameterDeclaration), getEffectiveConstraintOfTypeParameter)[0]; } function getInferredTypeParameterConstraint(typeParameter: TypeParameter) { @@ -29242,11 +29241,10 @@ namespace ts { const constraint = getEffectiveConstraintOfTypeParameter(source); const sourceConstraint = constraint && getTypeFromTypeNode(constraint); const targetConstraint = getConstraintOfTypeParameter(target); - if (sourceConstraint) { - // relax check if later interface augmentation has no constraint - if (!targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint)) { - return false; - } + // relax check if later interface augmentation has no constraint, it's more broad and is OK to merge with + // a more constrained interface (this could be generalized to a full heirarchy check, but that's maybe overkill) + if (sourceConstraint && targetConstraint && !isTypeIdenticalTo(sourceConstraint, targetConstraint)) { + return false; } // If the type parameter node has a default and it is not identical to the default diff --git a/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.js b/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.js new file mode 100644 index 0000000000000..a42411866397b --- /dev/null +++ b/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.js @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.ts] //// + +//// [working.ts] +// minmal samples from #33395 +export namespace ns { + interface Function any> { + throttle(): Function; + } + interface Function { + unary(): Function<() => ReturnType>; + } +} +//// [regression.ts] +export namespace ns { + interface Function { + unary(): Function<() => ReturnType>; + } + interface Function any> { + throttle(): Function; + } +} + +//// [working.js] +"use strict"; +exports.__esModule = true; +//// [regression.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.symbols b/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.symbols new file mode 100644 index 0000000000000..5dc01da7c4877 --- /dev/null +++ b/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.symbols @@ -0,0 +1,51 @@ +=== tests/cases/compiler/working.ts === +// minmal samples from #33395 +export namespace ns { +>ns : Symbol(ns, Decl(working.ts, 0, 0)) + + interface Function any> { +>Function : Symbol(Function, Decl(working.ts, 1, 21), Decl(working.ts, 4, 5)) +>T : Symbol(T, Decl(working.ts, 2, 23), Decl(working.ts, 5, 23)) +>args : Symbol(args, Decl(working.ts, 2, 34)) + + throttle(): Function; +>throttle : Symbol(Function.throttle, Decl(working.ts, 2, 57)) +>Function : Symbol(Function, Decl(working.ts, 1, 21), Decl(working.ts, 4, 5)) +>T : Symbol(T, Decl(working.ts, 2, 23), Decl(working.ts, 5, 23)) + } + interface Function { +>Function : Symbol(Function, Decl(working.ts, 1, 21), Decl(working.ts, 4, 5)) +>T : Symbol(T, Decl(working.ts, 2, 23), Decl(working.ts, 5, 23)) + + unary(): Function<() => ReturnType>; +>unary : Symbol(Function.unary, Decl(working.ts, 5, 27)) +>Function : Symbol(Function, Decl(working.ts, 1, 21), Decl(working.ts, 4, 5)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(working.ts, 2, 23), Decl(working.ts, 5, 23)) + } +} +=== tests/cases/compiler/regression.ts === +export namespace ns { +>ns : Symbol(ns, Decl(regression.ts, 0, 0)) + + interface Function { +>Function : Symbol(Function, Decl(regression.ts, 0, 21), Decl(regression.ts, 3, 5)) +>T : Symbol(T, Decl(regression.ts, 1, 23), Decl(regression.ts, 4, 23)) + + unary(): Function<() => ReturnType>; +>unary : Symbol(Function.unary, Decl(regression.ts, 1, 27)) +>Function : Symbol(Function, Decl(regression.ts, 0, 21), Decl(regression.ts, 3, 5)) +>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(regression.ts, 1, 23), Decl(regression.ts, 4, 23)) + } + interface Function any> { +>Function : Symbol(Function, Decl(regression.ts, 0, 21), Decl(regression.ts, 3, 5)) +>T : Symbol(T, Decl(regression.ts, 1, 23), Decl(regression.ts, 4, 23)) +>args : Symbol(args, Decl(regression.ts, 4, 34)) + + throttle(): Function; +>throttle : Symbol(Function.throttle, Decl(regression.ts, 4, 57)) +>Function : Symbol(Function, Decl(regression.ts, 0, 21), Decl(regression.ts, 3, 5)) +>T : Symbol(T, Decl(regression.ts, 1, 23), Decl(regression.ts, 4, 23)) + } +} diff --git a/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.types b/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.types new file mode 100644 index 0000000000000..e42e438c98321 --- /dev/null +++ b/tests/baselines/reference/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.types @@ -0,0 +1,27 @@ +=== tests/cases/compiler/working.ts === +// minmal samples from #33395 +export namespace ns { + interface Function any> { +>args : any + + throttle(): Function; +>throttle : () => Function + } + interface Function { + unary(): Function<() => ReturnType>; +>unary : () => Function<() => ReturnType> + } +} +=== tests/cases/compiler/regression.ts === +export namespace ns { + interface Function { + unary(): Function<() => ReturnType>; +>unary : () => Function<() => ReturnType> + } + interface Function any> { +>args : any + + throttle(): Function; +>throttle : () => Function + } +} diff --git a/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.errors.txt b/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.errors.txt index 21a35e0553b4c..edc33d6b1ba44 100644 --- a/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.errors.txt +++ b/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.errors.txt @@ -4,11 +4,9 @@ tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDi tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts(14,15): error TS2428: All declarations of 'B' must have identical type parameters. tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts(32,22): error TS2428: All declarations of 'A' must have identical type parameters. tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts(38,22): error TS2428: All declarations of 'A' must have identical type parameters. -tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts(53,11): error TS2428: All declarations of 'C' must have identical type parameters. -tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts(57,11): error TS2428: All declarations of 'C' must have identical type parameters. -==== tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts (8 errors) ==== +==== tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts (6 errors) ==== interface A { ~ !!! error TS2428: All declarations of 'A' must have identical type parameters. @@ -74,14 +72,10 @@ tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDi } interface C { - ~ -!!! error TS2428: All declarations of 'C' must have identical type parameters. x: T; } - interface C { // error - ~ -!!! error TS2428: All declarations of 'C' must have identical type parameters. + interface C { // ok y: T; } diff --git a/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.js b/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.js index 5b1964b2891ec..7e62915cb542e 100644 --- a/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.js +++ b/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.js @@ -55,7 +55,7 @@ interface C { x: T; } -interface C { // error +interface C { // ok y: T; } diff --git a/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.symbols b/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.symbols index a1ad41441bda4..6dd5de33354f6 100644 --- a/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.symbols +++ b/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.symbols @@ -137,7 +137,7 @@ interface C { >T : Symbol(T, Decl(twoGenericInterfacesWithDifferentConstraints.ts, 52, 12), Decl(twoGenericInterfacesWithDifferentConstraints.ts, 56, 12)) } -interface C { // error +interface C { // ok >C : Symbol(C, Decl(twoGenericInterfacesWithDifferentConstraints.ts, 50, 1), Decl(twoGenericInterfacesWithDifferentConstraints.ts, 54, 1)) >T : Symbol(T, Decl(twoGenericInterfacesWithDifferentConstraints.ts, 52, 12), Decl(twoGenericInterfacesWithDifferentConstraints.ts, 56, 12)) diff --git a/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.types b/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.types index 70a8be8cb3aa6..87877311b5376 100644 --- a/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.types +++ b/tests/baselines/reference/twoGenericInterfacesWithDifferentConstraints.types @@ -70,7 +70,7 @@ interface C { >x : T } -interface C { // error +interface C { // ok y: T; >y : T } diff --git a/tests/cases/compiler/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.ts b/tests/cases/compiler/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.ts new file mode 100644 index 0000000000000..7bbce8e8b0383 --- /dev/null +++ b/tests/cases/compiler/interfaceMergedUnconstrainedNoErrorIrrespectiveOfOrder.ts @@ -0,0 +1,19 @@ +// @filename: working.ts +// minmal samples from #33395 +export namespace ns { + interface Function any> { + throttle(): Function; + } + interface Function { + unary(): Function<() => ReturnType>; + } +} +// @filename: regression.ts +export namespace ns { + interface Function { + unary(): Function<() => ReturnType>; + } + interface Function any> { + throttle(): Function; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts b/tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts index 4c698626eb003..6963277915af1 100644 --- a/tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts +++ b/tests/cases/conformance/interfaces/declarationMerging/twoGenericInterfacesWithDifferentConstraints.ts @@ -54,7 +54,7 @@ interface C { x: T; } -interface C { // error +interface C { // ok y: T; }