diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6f643457e2f0f..9e5f5caec4d88 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12051,6 +12051,9 @@ namespace ts { if (((node.flags & NodeFlags.AccessibilityModifier) !== (otherAccessor.flags & NodeFlags.AccessibilityModifier))) { error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); } + if (((node.flags & NodeFlags.Abstract) !== (otherAccessor.flags & NodeFlags.Abstract))) { + error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract); + } const currentAccessorType = getAnnotatedAccessorType(node); const otherAccessorType = getAnnotatedAccessorType(otherAccessor); @@ -16498,8 +16501,11 @@ namespace ts { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "abstract"); } if (node.kind !== SyntaxKind.ClassDeclaration) { - if (node.kind !== SyntaxKind.MethodDeclaration) { - return grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_or_method_declaration); + if (node.kind !== SyntaxKind.MethodDeclaration && + node.kind !== SyntaxKind.PropertyDeclaration && + node.kind !== SyntaxKind.GetAccessor && + node.kind !== SyntaxKind.SetAccessor) { + return grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration); } if (!(node.parent.kind === SyntaxKind.ClassDeclaration && node.parent.flags & NodeFlags.Abstract)) { return grammarErrorOnNode(modifier, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class); @@ -16993,7 +16999,7 @@ namespace ts { else if (isInAmbientContext(accessor)) { return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context); } - else if (accessor.body === undefined) { + else if (accessor.body === undefined && !(accessor.flags & NodeFlags.Abstract)) { return grammarErrorAtPos(getSourceFileOfNode(accessor), accessor.end - 1, ";".length, Diagnostics._0_expected, "{"); } else if (accessor.typeParameters) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 70c6d8ff167f5..64e2974e1a124 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -779,7 +779,7 @@ "category": "Error", "code": 1241 }, - "'abstract' modifier can only appear on a class or method declaration.": { + "'abstract' modifier can only appear on a class, method, or property declaration.": { "category": "Error", "code": 1242 }, @@ -1843,6 +1843,10 @@ "category": "Error", "code": 2675 }, + "Accessors must both be abstract or non-abstract.": { + "category": "Error", + "code": 2676 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/tests/baselines/reference/abstractProperty.js b/tests/baselines/reference/abstractProperty.js new file mode 100644 index 0000000000000..9564be862dc09 --- /dev/null +++ b/tests/baselines/reference/abstractProperty.js @@ -0,0 +1,56 @@ +//// [abstractProperty.ts] +interface A { + prop: string; + raw: string; + m(): void; +} +abstract class B implements A { + abstract prop: string; + abstract raw: string; + abstract readonly ro: string; + abstract get readonlyProp(): string; + abstract set readonlyProp(val: string); + abstract m(): void; +} +class C extends B { + get prop() { return "foo"; } + set prop(v) { } + raw = "edge"; + readonly ro = "readonly please"; + readonlyProp: string; // don't have to give a value, in fact + m() { } +} + +//// [abstractProperty.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var B = (function () { + function B() { + } + Object.defineProperty(B.prototype, "readonlyProp", { + get: function () { }, + set: function (val) { }, + enumerable: true, + configurable: true + }); + return B; +}()); +var C = (function (_super) { + __extends(C, _super); + function C() { + _super.apply(this, arguments); + this.raw = "edge"; + this.ro = "readonly please"; + } + Object.defineProperty(C.prototype, "prop", { + get: function () { return "foo"; }, + set: function (v) { }, + enumerable: true, + configurable: true + }); + C.prototype.m = function () { }; + return C; +}(B)); diff --git a/tests/baselines/reference/abstractProperty.symbols b/tests/baselines/reference/abstractProperty.symbols new file mode 100644 index 0000000000000..f64df9a5cde0a --- /dev/null +++ b/tests/baselines/reference/abstractProperty.symbols @@ -0,0 +1,59 @@ +=== tests/cases/compiler/abstractProperty.ts === +interface A { +>A : Symbol(A, Decl(abstractProperty.ts, 0, 0)) + + prop: string; +>prop : Symbol(prop, Decl(abstractProperty.ts, 0, 13)) + + raw: string; +>raw : Symbol(raw, Decl(abstractProperty.ts, 1, 17)) + + m(): void; +>m : Symbol(m, Decl(abstractProperty.ts, 2, 16)) +} +abstract class B implements A { +>B : Symbol(B, Decl(abstractProperty.ts, 4, 1)) +>A : Symbol(A, Decl(abstractProperty.ts, 0, 0)) + + abstract prop: string; +>prop : Symbol(prop, Decl(abstractProperty.ts, 5, 31)) + + abstract raw: string; +>raw : Symbol(raw, Decl(abstractProperty.ts, 6, 26)) + + abstract readonly ro: string; +>ro : Symbol(ro, Decl(abstractProperty.ts, 7, 25)) + + abstract get readonlyProp(): string; +>readonlyProp : Symbol(readonlyProp, Decl(abstractProperty.ts, 8, 33), Decl(abstractProperty.ts, 9, 40)) + + abstract set readonlyProp(val: string); +>readonlyProp : Symbol(readonlyProp, Decl(abstractProperty.ts, 8, 33), Decl(abstractProperty.ts, 9, 40)) +>val : Symbol(val, Decl(abstractProperty.ts, 10, 30)) + + abstract m(): void; +>m : Symbol(m, Decl(abstractProperty.ts, 10, 43)) +} +class C extends B { +>C : Symbol(C, Decl(abstractProperty.ts, 12, 1)) +>B : Symbol(B, Decl(abstractProperty.ts, 4, 1)) + + get prop() { return "foo"; } +>prop : Symbol(prop, Decl(abstractProperty.ts, 13, 19), Decl(abstractProperty.ts, 14, 32)) + + set prop(v) { } +>prop : Symbol(prop, Decl(abstractProperty.ts, 13, 19), Decl(abstractProperty.ts, 14, 32)) +>v : Symbol(v, Decl(abstractProperty.ts, 15, 13)) + + raw = "edge"; +>raw : Symbol(raw, Decl(abstractProperty.ts, 15, 19)) + + readonly ro = "readonly please"; +>ro : Symbol(ro, Decl(abstractProperty.ts, 16, 17)) + + readonlyProp: string; // don't have to give a value, in fact +>readonlyProp : Symbol(readonlyProp, Decl(abstractProperty.ts, 17, 36)) + + m() { } +>m : Symbol(m, Decl(abstractProperty.ts, 18, 25)) +} diff --git a/tests/baselines/reference/abstractProperty.types b/tests/baselines/reference/abstractProperty.types new file mode 100644 index 0000000000000..b6aff9356cdb7 --- /dev/null +++ b/tests/baselines/reference/abstractProperty.types @@ -0,0 +1,62 @@ +=== tests/cases/compiler/abstractProperty.ts === +interface A { +>A : A + + prop: string; +>prop : string + + raw: string; +>raw : string + + m(): void; +>m : () => void +} +abstract class B implements A { +>B : B +>A : A + + abstract prop: string; +>prop : string + + abstract raw: string; +>raw : string + + abstract readonly ro: string; +>ro : string + + abstract get readonlyProp(): string; +>readonlyProp : string + + abstract set readonlyProp(val: string); +>readonlyProp : string +>val : string + + abstract m(): void; +>m : () => void +} +class C extends B { +>C : C +>B : B + + get prop() { return "foo"; } +>prop : string +>"foo" : string + + set prop(v) { } +>prop : string +>v : string + + raw = "edge"; +>raw : string +>"edge" : string + + readonly ro = "readonly please"; +>ro : string +>"readonly please" : string + + readonlyProp: string; // don't have to give a value, in fact +>readonlyProp : string + + m() { } +>m : () => void +} diff --git a/tests/baselines/reference/abstractPropertyNegative.errors.txt b/tests/baselines/reference/abstractPropertyNegative.errors.txt new file mode 100644 index 0000000000000..448fb2255e0bb --- /dev/null +++ b/tests/baselines/reference/abstractPropertyNegative.errors.txt @@ -0,0 +1,106 @@ +tests/cases/compiler/abstractPropertyNegative.ts(10,18): error TS2380: 'get' and 'set' accessor must have the same type. +tests/cases/compiler/abstractPropertyNegative.ts(11,18): error TS2380: 'get' and 'set' accessor must have the same type. +tests/cases/compiler/abstractPropertyNegative.ts(13,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'm' from class 'B'. +tests/cases/compiler/abstractPropertyNegative.ts(13,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'mismatch' from class 'B'. +tests/cases/compiler/abstractPropertyNegative.ts(13,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'prop' from class 'B'. +tests/cases/compiler/abstractPropertyNegative.ts(13,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'readonlyProp' from class 'B'. +tests/cases/compiler/abstractPropertyNegative.ts(15,5): error TS1244: Abstract methods can only appear within an abstract class. +tests/cases/compiler/abstractPropertyNegative.ts(16,37): error TS1005: '{' expected. +tests/cases/compiler/abstractPropertyNegative.ts(19,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property. +tests/cases/compiler/abstractPropertyNegative.ts(24,7): error TS2415: Class 'WrongTypePropertyImpl' incorrectly extends base class 'WrongTypeProperty'. + Types of property 'num' are incompatible. + Type 'string' is not assignable to type 'number'. +tests/cases/compiler/abstractPropertyNegative.ts(30,7): error TS2415: Class 'WrongTypeAccessorImpl' incorrectly extends base class 'WrongTypeAccessor'. + Types of property 'num' are incompatible. + Type 'string' is not assignable to type 'number'. +tests/cases/compiler/abstractPropertyNegative.ts(33,7): error TS2415: Class 'WrongTypeAccessorImpl2' incorrectly extends base class 'WrongTypeAccessor'. + Types of property 'num' are incompatible. + Type 'string' is not assignable to type 'number'. +tests/cases/compiler/abstractPropertyNegative.ts(38,18): error TS2676: Accessors must both be abstract or non-abstract. +tests/cases/compiler/abstractPropertyNegative.ts(39,9): error TS2676: Accessors must both be abstract or non-abstract. +tests/cases/compiler/abstractPropertyNegative.ts(40,9): error TS2676: Accessors must both be abstract or non-abstract. +tests/cases/compiler/abstractPropertyNegative.ts(41,18): error TS2676: Accessors must both be abstract or non-abstract. + + +==== tests/cases/compiler/abstractPropertyNegative.ts (16 errors) ==== + interface A { + prop: string; + m(): string; + } + abstract class B implements A { + abstract prop: string; + public abstract readonly ro: string; + abstract get readonlyProp(): string; + abstract m(): string; + abstract get mismatch(): string; + ~~~~~~~~ +!!! error TS2380: 'get' and 'set' accessor must have the same type. + abstract set mismatch(val: number); // error, not same type + ~~~~~~~~ +!!! error TS2380: 'get' and 'set' accessor must have the same type. + } + class C extends B { + ~ +!!! error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'm' from class 'B'. + ~ +!!! error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'mismatch' from class 'B'. + ~ +!!! error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'prop' from class 'B'. + ~ +!!! error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'readonlyProp' from class 'B'. + readonly ro = "readonly please"; + abstract notAllowed: string; + ~~~~~~~~ +!!! error TS1244: Abstract methods can only appear within an abstract class. + get concreteWithNoBody(): string; + ~ +!!! error TS1005: '{' expected. + } + let c = new C(); + c.ro = "error: lhs of assignment can't be readonly"; + ~~~~ +!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property. + + abstract class WrongTypeProperty { + abstract num: number; + } + class WrongTypePropertyImpl extends WrongTypeProperty { + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2415: Class 'WrongTypePropertyImpl' incorrectly extends base class 'WrongTypeProperty'. +!!! error TS2415: Types of property 'num' are incompatible. +!!! error TS2415: Type 'string' is not assignable to type 'number'. + num = "nope, wrong"; + } + abstract class WrongTypeAccessor { + abstract get num(): number; + } + class WrongTypeAccessorImpl extends WrongTypeAccessor { + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2415: Class 'WrongTypeAccessorImpl' incorrectly extends base class 'WrongTypeAccessor'. +!!! error TS2415: Types of property 'num' are incompatible. +!!! error TS2415: Type 'string' is not assignable to type 'number'. + get num() { return "nope, wrong"; } + } + class WrongTypeAccessorImpl2 extends WrongTypeAccessor { + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2415: Class 'WrongTypeAccessorImpl2' incorrectly extends base class 'WrongTypeAccessor'. +!!! error TS2415: Types of property 'num' are incompatible. +!!! error TS2415: Type 'string' is not assignable to type 'number'. + num = "nope, wrong"; + } + + abstract class AbstractAccessorMismatch { + abstract get p1(): string; + ~~ +!!! error TS2676: Accessors must both be abstract or non-abstract. + set p1(val: string) { }; + ~~ +!!! error TS2676: Accessors must both be abstract or non-abstract. + get p2(): string { return "should work"; } + ~~ +!!! error TS2676: Accessors must both be abstract or non-abstract. + abstract set p2(val: string); + ~~ +!!! error TS2676: Accessors must both be abstract or non-abstract. + } + \ No newline at end of file diff --git a/tests/baselines/reference/abstractPropertyNegative.js b/tests/baselines/reference/abstractPropertyNegative.js new file mode 100644 index 0000000000000..55c9aea941169 --- /dev/null +++ b/tests/baselines/reference/abstractPropertyNegative.js @@ -0,0 +1,144 @@ +//// [abstractPropertyNegative.ts] +interface A { + prop: string; + m(): string; +} +abstract class B implements A { + abstract prop: string; + public abstract readonly ro: string; + abstract get readonlyProp(): string; + abstract m(): string; + abstract get mismatch(): string; + abstract set mismatch(val: number); // error, not same type +} +class C extends B { + readonly ro = "readonly please"; + abstract notAllowed: string; + get concreteWithNoBody(): string; +} +let c = new C(); +c.ro = "error: lhs of assignment can't be readonly"; + +abstract class WrongTypeProperty { + abstract num: number; +} +class WrongTypePropertyImpl extends WrongTypeProperty { + num = "nope, wrong"; +} +abstract class WrongTypeAccessor { + abstract get num(): number; +} +class WrongTypeAccessorImpl extends WrongTypeAccessor { + get num() { return "nope, wrong"; } +} +class WrongTypeAccessorImpl2 extends WrongTypeAccessor { + num = "nope, wrong"; +} + +abstract class AbstractAccessorMismatch { + abstract get p1(): string; + set p1(val: string) { }; + get p2(): string { return "should work"; } + abstract set p2(val: string); +} + + +//// [abstractPropertyNegative.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var B = (function () { + function B() { + } + Object.defineProperty(B.prototype, "readonlyProp", { + get: function () { }, + enumerable: true, + configurable: true + }); + Object.defineProperty(B.prototype, "mismatch", { + get: function () { }, + set: function (val) { } // error, not same type + , + enumerable: true, + configurable: true + }); + return B; +}()); +var C = (function (_super) { + __extends(C, _super); + function C() { + _super.apply(this, arguments); + this.ro = "readonly please"; + } + Object.defineProperty(C.prototype, "concreteWithNoBody", { + get: function () { }, + enumerable: true, + configurable: true + }); + return C; +}(B)); +var c = new C(); +c.ro = "error: lhs of assignment can't be readonly"; +var WrongTypeProperty = (function () { + function WrongTypeProperty() { + } + return WrongTypeProperty; +}()); +var WrongTypePropertyImpl = (function (_super) { + __extends(WrongTypePropertyImpl, _super); + function WrongTypePropertyImpl() { + _super.apply(this, arguments); + this.num = "nope, wrong"; + } + return WrongTypePropertyImpl; +}(WrongTypeProperty)); +var WrongTypeAccessor = (function () { + function WrongTypeAccessor() { + } + Object.defineProperty(WrongTypeAccessor.prototype, "num", { + get: function () { }, + enumerable: true, + configurable: true + }); + return WrongTypeAccessor; +}()); +var WrongTypeAccessorImpl = (function (_super) { + __extends(WrongTypeAccessorImpl, _super); + function WrongTypeAccessorImpl() { + _super.apply(this, arguments); + } + Object.defineProperty(WrongTypeAccessorImpl.prototype, "num", { + get: function () { return "nope, wrong"; }, + enumerable: true, + configurable: true + }); + return WrongTypeAccessorImpl; +}(WrongTypeAccessor)); +var WrongTypeAccessorImpl2 = (function (_super) { + __extends(WrongTypeAccessorImpl2, _super); + function WrongTypeAccessorImpl2() { + _super.apply(this, arguments); + this.num = "nope, wrong"; + } + return WrongTypeAccessorImpl2; +}(WrongTypeAccessor)); +var AbstractAccessorMismatch = (function () { + function AbstractAccessorMismatch() { + } + Object.defineProperty(AbstractAccessorMismatch.prototype, "p1", { + get: function () { }, + set: function (val) { }, + enumerable: true, + configurable: true + }); + ; + Object.defineProperty(AbstractAccessorMismatch.prototype, "p2", { + get: function () { return "should work"; }, + set: function (val) { }, + enumerable: true, + configurable: true + }); + return AbstractAccessorMismatch; +}()); diff --git a/tests/baselines/reference/classAbstractConstructor.errors.txt b/tests/baselines/reference/classAbstractConstructor.errors.txt index fee9f4cefee0d..c57d52a018fcb 100644 --- a/tests/baselines/reference/classAbstractConstructor.errors.txt +++ b/tests/baselines/reference/classAbstractConstructor.errors.txt @@ -1,9 +1,9 @@ -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructor.ts(2,5): error TS1242: 'abstract' modifier can only appear on a class or method declaration. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructor.ts(2,5): error TS1242: 'abstract' modifier can only appear on a class, method, or property declaration. ==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructor.ts (1 errors) ==== abstract class A { abstract constructor() {} ~~~~~~~~ -!!! error TS1242: 'abstract' modifier can only appear on a class or method declaration. +!!! error TS1242: 'abstract' modifier can only appear on a class, method, or property declaration. } \ No newline at end of file diff --git a/tests/baselines/reference/classAbstractDeclarations.d.errors.txt b/tests/baselines/reference/classAbstractDeclarations.d.errors.txt index a1c0e55152c36..d71f53118af7f 100644 --- a/tests/baselines/reference/classAbstractDeclarations.d.errors.txt +++ b/tests/baselines/reference/classAbstractDeclarations.d.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractDeclarations.d.ts(2,5): error TS1242: 'abstract' modifier can only appear on a class or method declaration. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractDeclarations.d.ts(2,5): error TS1242: 'abstract' modifier can only appear on a class, method, or property declaration. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractDeclarations.d.ts(2,28): error TS1183: An implementation cannot be declared in ambient contexts. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractDeclarations.d.ts(11,15): error TS2515: Non-abstract class 'CC' does not implement inherited abstract member 'foo' from class 'AA'. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractDeclarations.d.ts(13,15): error TS2515: Non-abstract class 'DD' does not implement inherited abstract member 'foo' from class 'BB'. @@ -9,7 +9,7 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbst declare abstract class A { abstract constructor() {} ~~~~~~~~ -!!! error TS1242: 'abstract' modifier can only appear on a class or method declaration. +!!! error TS1242: 'abstract' modifier can only appear on a class, method, or property declaration. ~ !!! error TS1183: An implementation cannot be declared in ambient contexts. } diff --git a/tests/baselines/reference/classAbstractProperties.errors.txt b/tests/baselines/reference/classAbstractProperties.errors.txt index 223a3049825b8..88c0b587484be 100644 --- a/tests/baselines/reference/classAbstractProperties.errors.txt +++ b/tests/baselines/reference/classAbstractProperties.errors.txt @@ -1,29 +1,17 @@ -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts(2,5): error TS1242: 'abstract' modifier can only appear on a class or method declaration. -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts(3,12): error TS1242: 'abstract' modifier can only appear on a class or method declaration. -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts(4,15): error TS1242: 'abstract' modifier can only appear on a class or method declaration. -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts(5,13): error TS1242: 'abstract' modifier can only appear on a class or method declaration. -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts(7,5): error TS1242: 'abstract' modifier can only appear on a class or method declaration. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts(5,13): error TS1243: 'private' modifier cannot be used with 'abstract' modifier. tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts(12,13): error TS1243: 'private' modifier cannot be used with 'abstract' modifier. -==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts (6 errors) ==== +==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts (2 errors) ==== abstract class A { abstract x : number; - ~~~~~~~~ -!!! error TS1242: 'abstract' modifier can only appear on a class or method declaration. public abstract y : number; - ~~~~~~~~ -!!! error TS1242: 'abstract' modifier can only appear on a class or method declaration. protected abstract z : number; - ~~~~~~~~ -!!! error TS1242: 'abstract' modifier can only appear on a class or method declaration. private abstract w : number; ~~~~~~~~ -!!! error TS1242: 'abstract' modifier can only appear on a class or method declaration. +!!! error TS1243: 'private' modifier cannot be used with 'abstract' modifier. abstract m: () => void; - ~~~~~~~~ -!!! error TS1242: 'abstract' modifier can only appear on a class or method declaration. abstract foo_x() : number; public abstract foo_y() : number; diff --git a/tests/baselines/reference/classAbstractWithInterface.errors.txt b/tests/baselines/reference/classAbstractWithInterface.errors.txt index fa0a3d089573d..0fe3b3ba3424c 100644 --- a/tests/baselines/reference/classAbstractWithInterface.errors.txt +++ b/tests/baselines/reference/classAbstractWithInterface.errors.txt @@ -1,7 +1,7 @@ -tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractWithInterface.ts(1,1): error TS1242: 'abstract' modifier can only appear on a class or method declaration. +tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractWithInterface.ts(1,1): error TS1242: 'abstract' modifier can only appear on a class, method, or property declaration. ==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractWithInterface.ts (1 errors) ==== abstract interface I {} ~~~~~~~~ -!!! error TS1242: 'abstract' modifier can only appear on a class or method declaration. \ No newline at end of file +!!! error TS1242: 'abstract' modifier can only appear on a class, method, or property declaration. \ No newline at end of file diff --git a/tests/cases/compiler/abstractProperty.ts b/tests/cases/compiler/abstractProperty.ts new file mode 100644 index 0000000000000..c0b76057a6b08 --- /dev/null +++ b/tests/cases/compiler/abstractProperty.ts @@ -0,0 +1,22 @@ +//@target: ES5 +interface A { + prop: string; + raw: string; + m(): void; +} +abstract class B implements A { + abstract prop: string; + abstract raw: string; + abstract readonly ro: string; + abstract get readonlyProp(): string; + abstract set readonlyProp(val: string); + abstract m(): void; +} +class C extends B { + get prop() { return "foo"; } + set prop(v) { } + raw = "edge"; + readonly ro = "readonly please"; + readonlyProp: string; // don't have to give a value, in fact + m() { } +} \ No newline at end of file diff --git a/tests/cases/compiler/abstractPropertyNegative.ts b/tests/cases/compiler/abstractPropertyNegative.ts new file mode 100644 index 0000000000000..16c09b105734b --- /dev/null +++ b/tests/cases/compiler/abstractPropertyNegative.ts @@ -0,0 +1,43 @@ +//@target: ES5 +interface A { + prop: string; + m(): string; +} +abstract class B implements A { + abstract prop: string; + public abstract readonly ro: string; + abstract get readonlyProp(): string; + abstract m(): string; + abstract get mismatch(): string; + abstract set mismatch(val: number); // error, not same type +} +class C extends B { + readonly ro = "readonly please"; + abstract notAllowed: string; + get concreteWithNoBody(): string; +} +let c = new C(); +c.ro = "error: lhs of assignment can't be readonly"; + +abstract class WrongTypeProperty { + abstract num: number; +} +class WrongTypePropertyImpl extends WrongTypeProperty { + num = "nope, wrong"; +} +abstract class WrongTypeAccessor { + abstract get num(): number; +} +class WrongTypeAccessorImpl extends WrongTypeAccessor { + get num() { return "nope, wrong"; } +} +class WrongTypeAccessorImpl2 extends WrongTypeAccessor { + num = "nope, wrong"; +} + +abstract class AbstractAccessorMismatch { + abstract get p1(): string; + set p1(val: string) { }; + get p2(): string { return "should work"; } + abstract set p2(val: string); +}