From e2f2393dbce0fca76ff4dd08937abd954434054c Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Mon, 9 Aug 2021 21:50:37 +0000 Subject: [PATCH] Cherry-pick PR #45352 into release-4.4 Component commits: 58a924d968 fix(45345): throw an error on overridden member that is defined in multiple interfaces --- src/compiler/checker.ts | 4 +- .../baselines/reference/override20.errors.txt | 41 ++++++++++++ tests/baselines/reference/override20.js | 47 +++++++++++++ tests/baselines/reference/override20.symbols | 67 +++++++++++++++++++ tests/baselines/reference/override20.types | 61 +++++++++++++++++ .../cases/conformance/override/override20.ts | 34 ++++++++++ .../fourslash/codeFixOverrideModifier22.ts | 41 ++++++++++++ 7 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/override20.errors.txt create mode 100644 tests/baselines/reference/override20.js create mode 100644 tests/baselines/reference/override20.symbols create mode 100644 tests/baselines/reference/override20.types create mode 100644 tests/cases/conformance/override/override20.ts create mode 100644 tests/cases/fourslash/codeFixOverrideModifier22.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c470ac4c0ddfe..179460eb1ee8e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -37687,8 +37687,8 @@ namespace ts { error(member, Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1, baseClassName, symbolToString(suggestion)) : error(member, Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0, baseClassName); } - else if (prop && baseProp?.valueDeclaration && compilerOptions.noImplicitOverride && !nodeInAmbientContext) { - const baseHasAbstract = hasAbstractModifier(baseProp.valueDeclaration); + else if (prop && baseProp?.declarations && compilerOptions.noImplicitOverride && !nodeInAmbientContext) { + const baseHasAbstract = some(baseProp.declarations, d => hasAbstractModifier(d)); if (hasOverride) { return; } diff --git a/tests/baselines/reference/override20.errors.txt b/tests/baselines/reference/override20.errors.txt new file mode 100644 index 0000000000000..42250ca3ac47e --- /dev/null +++ b/tests/baselines/reference/override20.errors.txt @@ -0,0 +1,41 @@ +tests/cases/conformance/override/override20.ts(25,5): error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'I1 & I2'. +tests/cases/conformance/override/override20.ts(28,5): error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'I1 & I2'. + + +==== tests/cases/conformance/override/override20.ts (2 errors) ==== + const Foo: C1 & C2 = + class { + m1() { } + m2() { } + } + + interface I1 { + m1(): void; + } + + interface I2 { + m1(): void; + m2(): void; + } + + interface C1 { + new(...args: any[]): I1; + } + + interface C2 { + new(...args: any[]): I2; + } + + export class Bar extends Foo { + m1() { + ~~ +!!! error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'I1 & I2'. + super.m1(); + } + m2() { + ~~ +!!! error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'I1 & I2'. + super.m2(); + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/override20.js b/tests/baselines/reference/override20.js new file mode 100644 index 0000000000000..7e6a8f87881d2 --- /dev/null +++ b/tests/baselines/reference/override20.js @@ -0,0 +1,47 @@ +//// [override20.ts] +const Foo: C1 & C2 = + class { + m1() { } + m2() { } + } + +interface I1 { + m1(): void; +} + +interface I2 { + m1(): void; + m2(): void; +} + +interface C1 { + new(...args: any[]): I1; +} + +interface C2 { + new(...args: any[]): I2; +} + +export class Bar extends Foo { + m1() { + super.m1(); + } + m2() { + super.m2(); + } +} + + +//// [override20.js] +const Foo = class { + m1() { } + m2() { } +}; +export class Bar extends Foo { + m1() { + super.m1(); + } + m2() { + super.m2(); + } +} diff --git a/tests/baselines/reference/override20.symbols b/tests/baselines/reference/override20.symbols new file mode 100644 index 0000000000000..69d598e4bb1f3 --- /dev/null +++ b/tests/baselines/reference/override20.symbols @@ -0,0 +1,67 @@ +=== tests/cases/conformance/override/override20.ts === +const Foo: C1 & C2 = +>Foo : Symbol(Foo, Decl(override20.ts, 0, 5)) +>C1 : Symbol(C1, Decl(override20.ts, 13, 1)) +>C2 : Symbol(C2, Decl(override20.ts, 17, 1)) + + class { + m1() { } +>m1 : Symbol(Foo.m1, Decl(override20.ts, 1, 11)) + + m2() { } +>m2 : Symbol(Foo.m2, Decl(override20.ts, 2, 16)) + } + +interface I1 { +>I1 : Symbol(I1, Decl(override20.ts, 4, 5)) + + m1(): void; +>m1 : Symbol(I1.m1, Decl(override20.ts, 6, 14)) +} + +interface I2 { +>I2 : Symbol(I2, Decl(override20.ts, 8, 1)) + + m1(): void; +>m1 : Symbol(I2.m1, Decl(override20.ts, 10, 14)) + + m2(): void; +>m2 : Symbol(I2.m2, Decl(override20.ts, 11, 15)) +} + +interface C1 { +>C1 : Symbol(C1, Decl(override20.ts, 13, 1)) + + new(...args: any[]): I1; +>args : Symbol(args, Decl(override20.ts, 16, 8)) +>I1 : Symbol(I1, Decl(override20.ts, 4, 5)) +} + +interface C2 { +>C2 : Symbol(C2, Decl(override20.ts, 17, 1)) + + new(...args: any[]): I2; +>args : Symbol(args, Decl(override20.ts, 20, 8)) +>I2 : Symbol(I2, Decl(override20.ts, 8, 1)) +} + +export class Bar extends Foo { +>Bar : Symbol(Bar, Decl(override20.ts, 21, 1)) +>Foo : Symbol(Foo, Decl(override20.ts, 0, 5)) + + m1() { +>m1 : Symbol(Bar.m1, Decl(override20.ts, 23, 30)) + + super.m1(); +>super.m1 : Symbol(m1, Decl(override20.ts, 6, 14), Decl(override20.ts, 10, 14)) +>m1 : Symbol(m1, Decl(override20.ts, 6, 14), Decl(override20.ts, 10, 14)) + } + m2() { +>m2 : Symbol(Bar.m2, Decl(override20.ts, 26, 5)) + + super.m2(); +>super.m2 : Symbol(I2.m2, Decl(override20.ts, 11, 15)) +>m2 : Symbol(I2.m2, Decl(override20.ts, 11, 15)) + } +} + diff --git a/tests/baselines/reference/override20.types b/tests/baselines/reference/override20.types new file mode 100644 index 0000000000000..70406a710d184 --- /dev/null +++ b/tests/baselines/reference/override20.types @@ -0,0 +1,61 @@ +=== tests/cases/conformance/override/override20.ts === +const Foo: C1 & C2 = +>Foo : C1 & C2 + + class { +>class { m1() { } m2() { } } : typeof Foo + + m1() { } +>m1 : () => void + + m2() { } +>m2 : () => void + } + +interface I1 { + m1(): void; +>m1 : () => void +} + +interface I2 { + m1(): void; +>m1 : () => void + + m2(): void; +>m2 : () => void +} + +interface C1 { + new(...args: any[]): I1; +>args : any[] +} + +interface C2 { + new(...args: any[]): I2; +>args : any[] +} + +export class Bar extends Foo { +>Bar : Bar +>Foo : I1 & I2 + + m1() { +>m1 : () => void + + super.m1(); +>super.m1() : void +>super.m1 : (() => void) & (() => void) +>super : I1 & I2 +>m1 : (() => void) & (() => void) + } + m2() { +>m2 : () => void + + super.m2(); +>super.m2() : void +>super.m2 : () => void +>super : I1 & I2 +>m2 : () => void + } +} + diff --git a/tests/cases/conformance/override/override20.ts b/tests/cases/conformance/override/override20.ts new file mode 100644 index 0000000000000..9835cd9135a05 --- /dev/null +++ b/tests/cases/conformance/override/override20.ts @@ -0,0 +1,34 @@ +// @target: esnext +// @noImplicitOverride: true + +const Foo: C1 & C2 = + class { + m1() { } + m2() { } + } + +interface I1 { + m1(): void; +} + +interface I2 { + m1(): void; + m2(): void; +} + +interface C1 { + new(...args: any[]): I1; +} + +interface C2 { + new(...args: any[]): I2; +} + +export class Bar extends Foo { + m1() { + super.m1(); + } + m2() { + super.m2(); + } +} diff --git a/tests/cases/fourslash/codeFixOverrideModifier22.ts b/tests/cases/fourslash/codeFixOverrideModifier22.ts new file mode 100644 index 0000000000000..4890a667f5334 --- /dev/null +++ b/tests/cases/fourslash/codeFixOverrideModifier22.ts @@ -0,0 +1,41 @@ +/// + +// @noImplicitOverride: true + +////const Foo: C1 & C2 = +//// class { +//// m1() { } +//// m2() { } +//// } +//// +////interface I1 { +//// m1(): void; +////} +//// +////interface I2 { +//// m1(): void; +//// m2(): void; +////} +//// +////interface C1 { +//// new(...args: any[]): I1; +////} +//// +////interface C2 { +//// new(...args: any[]): I2; +////} +//// +////class Bar extends Foo { +//// [|m1()|] { +//// super.m1(); +//// } +//// m2() { +//// super.m2(); +//// } +////} + +verify.codeFix({ + description: "Add 'override' modifier", + newRangeContent: "override m1()", + index: 0 +})