Skip to content

Abstract properties #7184

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
6 changes: 5 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down Expand Up @@ -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
Expand Down
56 changes: 56 additions & 0 deletions tests/baselines/reference/abstractProperty.js
Original file line number Diff line number Diff line change
@@ -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));
59 changes: 59 additions & 0 deletions tests/baselines/reference/abstractProperty.symbols
Original file line number Diff line number Diff line change
@@ -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))
}
62 changes: 62 additions & 0 deletions tests/baselines/reference/abstractProperty.types
Original file line number Diff line number Diff line change
@@ -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
}
106 changes: 106 additions & 0 deletions tests/baselines/reference/abstractPropertyNegative.errors.txt
Original file line number Diff line number Diff line change
@@ -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.
}

Loading