diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 781afe2cf9a84..530e734a552a2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36943,7 +36943,7 @@ namespace ts { } } - checkMembersForMissingOverrideModifier(node, type, typeWithThis); + checkMembersForMissingOverrideModifier(node, type, typeWithThis, staticType); const implementedTypeNodes = getEffectiveImplementsTypeNodes(node); if (implementedTypeNodes) { @@ -36980,13 +36980,18 @@ namespace ts { } } - function checkMembersForMissingOverrideModifier(node: ClassLikeDeclaration, type: InterfaceType, typeWithThis: Type) { + function checkMembersForMissingOverrideModifier(node: ClassLikeDeclaration, type: InterfaceType, typeWithThis: Type, staticType: ObjectType) { const nodeInAmbientContext = !!(node.flags & NodeFlags.Ambient); const baseTypeNode = getEffectiveBaseTypeNode(node); const baseTypes = baseTypeNode && getBaseTypes(type); const baseWithThis = baseTypes?.length ? getTypeWithThisArgument(first(baseTypes), type.thisType) : undefined; + const baseStaticType = getBaseConstructorTypeOfClass(type); for (const member of node.members) { + if (hasAmbientModifier(member)) { + continue; + } + if (isConstructorDeclaration(member)) { forEach(member.parameters, param => { if (isParameterPropertyDeclaration(param, member)) { @@ -36996,17 +37001,22 @@ namespace ts { } checkClassMember(member); } + function checkClassMember(member: ClassElement | ParameterPropertyDeclaration, memberIsParameterProperty?: boolean) { const hasOverride = hasOverrideModifier(member); + const hasStatic = hasStaticModifier(member); if (baseWithThis && (hasOverride || compilerOptions.noImplicitOverride)) { const declaredProp = member.name && getSymbolAtLocation(member.name) || getSymbolAtLocation(member); if (!declaredProp) { return; } + const thisType = hasStatic ? staticType : typeWithThis; + const baseType = hasStatic ? baseStaticType : baseWithThis; + const prop = getPropertyOfType(thisType, declaredProp.escapedName); + const baseProp = getPropertyOfType(baseType, declaredProp.escapedName); + const baseClassName = typeToString(baseWithThis); - const prop = getPropertyOfType(typeWithThis, declaredProp.escapedName); - const baseProp = getPropertyOfType(baseWithThis, declaredProp.escapedName); if (prop && !baseProp && hasOverride) { error(member, Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0, baseClassName); } @@ -40443,9 +40453,6 @@ namespace ts { else if (flags & ModifierFlags.Ambient) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "override", "declare"); } - else if (flags & ModifierFlags.Static) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "override"); - } else if (flags & ModifierFlags.Readonly) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "override", "readonly"); } @@ -40500,9 +40507,6 @@ namespace ts { else if (flags & ModifierFlags.Readonly) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "readonly"); } - else if (flags & ModifierFlags.Override) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_with_1_modifier, "static", "override"); - } else if (flags & ModifierFlags.Async) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async"); } diff --git a/tests/baselines/reference/override13.errors.txt b/tests/baselines/reference/override13.errors.txt new file mode 100644 index 0000000000000..3cc182018cdeb --- /dev/null +++ b/tests/baselines/reference/override13.errors.txt @@ -0,0 +1,41 @@ +tests/cases/conformance/override/override13.ts(7,5): error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Foo'. +tests/cases/conformance/override/override13.ts(13,12): error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Foo'. +tests/cases/conformance/override/override13.ts(19,5): error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Intermediate'. +tests/cases/conformance/override/override13.ts(25,12): error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Intermediate'. + + +==== tests/cases/conformance/override/override13.ts (4 errors) ==== + class Foo { + property = 1 + static staticProperty = 2 + } + + class SubFoo extends Foo { + property = 42; + ~~~~~~~~ +!!! error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Foo'. + staticProperty = 42; + } + + class StaticSubFoo extends Foo { + static property = 42; + static staticProperty = 42; + ~~~~~~~~~~~~~~ +!!! error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Foo'. + } + + class Intermediate extends Foo {} + + class Derived extends Intermediate { + property = 42; + ~~~~~~~~ +!!! error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Intermediate'. + staticProperty = 42; + } + + class StaticDerived extends Intermediate { + static property = 42; + static staticProperty = 42; + ~~~~~~~~~~~~~~ +!!! error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Intermediate'. + } \ No newline at end of file diff --git a/tests/baselines/reference/override13.js b/tests/baselines/reference/override13.js new file mode 100644 index 0000000000000..96864a74142e1 --- /dev/null +++ b/tests/baselines/reference/override13.js @@ -0,0 +1,51 @@ +//// [override13.ts] +class Foo { + property = 1 + static staticProperty = 2 +} + +class SubFoo extends Foo { + property = 42; + staticProperty = 42; +} + +class StaticSubFoo extends Foo { + static property = 42; + static staticProperty = 42; +} + +class Intermediate extends Foo {} + +class Derived extends Intermediate { + property = 42; + staticProperty = 42; +} + +class StaticDerived extends Intermediate { + static property = 42; + static staticProperty = 42; +} + +//// [override13.js] +class Foo { + property = 1; + static staticProperty = 2; +} +class SubFoo extends Foo { + property = 42; + staticProperty = 42; +} +class StaticSubFoo extends Foo { + static property = 42; + static staticProperty = 42; +} +class Intermediate extends Foo { +} +class Derived extends Intermediate { + property = 42; + staticProperty = 42; +} +class StaticDerived extends Intermediate { + static property = 42; + static staticProperty = 42; +} diff --git a/tests/baselines/reference/override13.symbols b/tests/baselines/reference/override13.symbols new file mode 100644 index 0000000000000..70a377a2b8bee --- /dev/null +++ b/tests/baselines/reference/override13.symbols @@ -0,0 +1,58 @@ +=== tests/cases/conformance/override/override13.ts === +class Foo { +>Foo : Symbol(Foo, Decl(override13.ts, 0, 0)) + + property = 1 +>property : Symbol(Foo.property, Decl(override13.ts, 0, 11)) + + static staticProperty = 2 +>staticProperty : Symbol(Foo.staticProperty, Decl(override13.ts, 1, 16)) +} + +class SubFoo extends Foo { +>SubFoo : Symbol(SubFoo, Decl(override13.ts, 3, 1)) +>Foo : Symbol(Foo, Decl(override13.ts, 0, 0)) + + property = 42; +>property : Symbol(SubFoo.property, Decl(override13.ts, 5, 26)) + + staticProperty = 42; +>staticProperty : Symbol(SubFoo.staticProperty, Decl(override13.ts, 6, 18)) +} + +class StaticSubFoo extends Foo { +>StaticSubFoo : Symbol(StaticSubFoo, Decl(override13.ts, 8, 1)) +>Foo : Symbol(Foo, Decl(override13.ts, 0, 0)) + + static property = 42; +>property : Symbol(StaticSubFoo.property, Decl(override13.ts, 10, 32)) + + static staticProperty = 42; +>staticProperty : Symbol(StaticSubFoo.staticProperty, Decl(override13.ts, 11, 25)) +} + +class Intermediate extends Foo {} +>Intermediate : Symbol(Intermediate, Decl(override13.ts, 13, 1)) +>Foo : Symbol(Foo, Decl(override13.ts, 0, 0)) + +class Derived extends Intermediate { +>Derived : Symbol(Derived, Decl(override13.ts, 15, 33)) +>Intermediate : Symbol(Intermediate, Decl(override13.ts, 13, 1)) + + property = 42; +>property : Symbol(Derived.property, Decl(override13.ts, 17, 36)) + + staticProperty = 42; +>staticProperty : Symbol(Derived.staticProperty, Decl(override13.ts, 18, 18)) +} + +class StaticDerived extends Intermediate { +>StaticDerived : Symbol(StaticDerived, Decl(override13.ts, 20, 1)) +>Intermediate : Symbol(Intermediate, Decl(override13.ts, 13, 1)) + + static property = 42; +>property : Symbol(StaticDerived.property, Decl(override13.ts, 22, 42)) + + static staticProperty = 42; +>staticProperty : Symbol(StaticDerived.staticProperty, Decl(override13.ts, 23, 25)) +} diff --git a/tests/baselines/reference/override13.types b/tests/baselines/reference/override13.types new file mode 100644 index 0000000000000..44b59c3f4ae39 --- /dev/null +++ b/tests/baselines/reference/override13.types @@ -0,0 +1,68 @@ +=== tests/cases/conformance/override/override13.ts === +class Foo { +>Foo : Foo + + property = 1 +>property : number +>1 : 1 + + static staticProperty = 2 +>staticProperty : number +>2 : 2 +} + +class SubFoo extends Foo { +>SubFoo : SubFoo +>Foo : Foo + + property = 42; +>property : number +>42 : 42 + + staticProperty = 42; +>staticProperty : number +>42 : 42 +} + +class StaticSubFoo extends Foo { +>StaticSubFoo : StaticSubFoo +>Foo : Foo + + static property = 42; +>property : number +>42 : 42 + + static staticProperty = 42; +>staticProperty : number +>42 : 42 +} + +class Intermediate extends Foo {} +>Intermediate : Intermediate +>Foo : Foo + +class Derived extends Intermediate { +>Derived : Derived +>Intermediate : Intermediate + + property = 42; +>property : number +>42 : 42 + + staticProperty = 42; +>staticProperty : number +>42 : 42 +} + +class StaticDerived extends Intermediate { +>StaticDerived : StaticDerived +>Intermediate : Intermediate + + static property = 42; +>property : number +>42 : 42 + + static staticProperty = 42; +>staticProperty : number +>42 : 42 +} diff --git a/tests/baselines/reference/override14.js b/tests/baselines/reference/override14.js new file mode 100644 index 0000000000000..df69367bc5c7e --- /dev/null +++ b/tests/baselines/reference/override14.js @@ -0,0 +1,16 @@ +//// [override14.ts] +class Foo { + property = 1 +} + +class SubFoo extends Foo { + declare property: number +} + + +//// [override14.js] +class Foo { + property = 1; +} +class SubFoo extends Foo { +} diff --git a/tests/baselines/reference/override14.symbols b/tests/baselines/reference/override14.symbols new file mode 100644 index 0000000000000..483f1997c49f3 --- /dev/null +++ b/tests/baselines/reference/override14.symbols @@ -0,0 +1,16 @@ +=== tests/cases/conformance/override/override14.ts === +class Foo { +>Foo : Symbol(Foo, Decl(override14.ts, 0, 0)) + + property = 1 +>property : Symbol(Foo.property, Decl(override14.ts, 0, 11)) +} + +class SubFoo extends Foo { +>SubFoo : Symbol(SubFoo, Decl(override14.ts, 2, 1)) +>Foo : Symbol(Foo, Decl(override14.ts, 0, 0)) + + declare property: number +>property : Symbol(SubFoo.property, Decl(override14.ts, 4, 26)) +} + diff --git a/tests/baselines/reference/override14.types b/tests/baselines/reference/override14.types new file mode 100644 index 0000000000000..d566a28db2635 --- /dev/null +++ b/tests/baselines/reference/override14.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/override/override14.ts === +class Foo { +>Foo : Foo + + property = 1 +>property : number +>1 : 1 +} + +class SubFoo extends Foo { +>SubFoo : SubFoo +>Foo : Foo + + declare property: number +>property : number +} + diff --git a/tests/baselines/reference/override5.errors.txt b/tests/baselines/reference/override5.errors.txt index 6c9c93aa1986c..e0dd689d47e39 100644 --- a/tests/baselines/reference/override5.errors.txt +++ b/tests/baselines/reference/override5.errors.txt @@ -1,7 +1,6 @@ -tests/cases/conformance/override/override5.ts(12,13): error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'B'. tests/cases/conformance/override/override5.ts(14,14): error TS1040: 'override' modifier cannot be used in an ambient context. tests/cases/conformance/override/override5.ts(16,14): error TS1029: 'override' modifier must precede 'readonly' modifier. -tests/cases/conformance/override/override5.ts(20,14): error TS1243: 'static' modifier cannot be used with 'override' modifier. +tests/cases/conformance/override/override5.ts(20,21): error TS4113: This member cannot have an 'override' modifier because it is not declared in the base class 'B'. tests/cases/conformance/override/override5.ts(22,14): error TS1030: 'override' modifier already seen. tests/cases/conformance/override/override5.ts(25,14): error TS1029: 'public' modifier must precede 'override' modifier. tests/cases/conformance/override/override5.ts(27,5): error TS1089: 'override' modifier cannot appear on a constructor declaration. @@ -9,7 +8,7 @@ tests/cases/conformance/override/override5.ts(44,23): error TS4112: This member tests/cases/conformance/override/override5.ts(45,23): error TS4112: This member cannot have an 'override' modifier because its containing class 'AND' does not extend another class. -==== tests/cases/conformance/override/override5.ts (9 errors) ==== +==== tests/cases/conformance/override/override5.ts (8 errors) ==== class B { p1: number = 1; p2: number = 2; @@ -22,8 +21,6 @@ tests/cases/conformance/override/override5.ts(45,23): error TS4112: This member class D extends B{ declare p1: number - ~~ -!!! error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'B'. override declare p2: number; ~~~~~~~ @@ -36,8 +33,8 @@ tests/cases/conformance/override/override5.ts(45,23): error TS4112: This member override readonly p4: number; override static sp: number; - ~~~~~~ -!!! error TS1243: 'static' modifier cannot be used with 'override' modifier. + ~~ +!!! error TS4113: This member cannot have an 'override' modifier because it is not declared in the base class 'B'. override override oop: number; ~~~~~~~~ diff --git a/tests/baselines/reference/override7.errors.txt b/tests/baselines/reference/override7.errors.txt index dec798252a28a..e5d908c99920c 100644 --- a/tests/baselines/reference/override7.errors.txt +++ b/tests/baselines/reference/override7.errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/override/override7.ts(11,14): error TS1040: 'override' modifier cannot be used in an ambient context. tests/cases/conformance/override/override7.ts(13,14): error TS1029: 'override' modifier must precede 'readonly' modifier. -tests/cases/conformance/override/override7.ts(17,14): error TS1243: 'static' modifier cannot be used with 'override' modifier. +tests/cases/conformance/override/override7.ts(17,21): error TS4113: This member cannot have an 'override' modifier because it is not declared in the base class 'B'. tests/cases/conformance/override/override7.ts(19,14): error TS1030: 'override' modifier already seen. tests/cases/conformance/override/override7.ts(19,23): error TS4113: This member cannot have an 'override' modifier because it is not declared in the base class 'B'. tests/cases/conformance/override/override7.ts(21,21): error TS4113: This member cannot have an 'override' modifier because it is not declared in the base class 'B'. @@ -33,8 +33,8 @@ tests/cases/conformance/override/override7.ts(42,23): error TS4112: This member override readonly p4: number; override static sp: number; - ~~~~~~ -!!! error TS1243: 'static' modifier cannot be used with 'override' modifier. + ~~ +!!! error TS4113: This member cannot have an 'override' modifier because it is not declared in the base class 'B'. override override oop: number; ~~~~~~~~ diff --git a/tests/cases/conformance/override/override13.ts b/tests/cases/conformance/override/override13.ts new file mode 100644 index 0000000000000..855df36af4a6e --- /dev/null +++ b/tests/cases/conformance/override/override13.ts @@ -0,0 +1,29 @@ +// @noImplicitOverride: true +// @target: esnext + +class Foo { + property = 1 + static staticProperty = 2 +} + +class SubFoo extends Foo { + property = 42; + staticProperty = 42; +} + +class StaticSubFoo extends Foo { + static property = 42; + static staticProperty = 42; +} + +class Intermediate extends Foo {} + +class Derived extends Intermediate { + property = 42; + staticProperty = 42; +} + +class StaticDerived extends Intermediate { + static property = 42; + static staticProperty = 42; +} \ No newline at end of file diff --git a/tests/cases/conformance/override/override14.ts b/tests/cases/conformance/override/override14.ts new file mode 100644 index 0000000000000..8fe638ba745d4 --- /dev/null +++ b/tests/cases/conformance/override/override14.ts @@ -0,0 +1,10 @@ +// @noImplicitOverride: true +// @target: esnext + +class Foo { + property = 1 +} + +class SubFoo extends Foo { + declare property: number +}