Skip to content

Commit ff233a9

Browse files
Variant accessors (#42425)
Allows accessors to have different access modifiers and types Fixes #2845, #2521
1 parent 2f0c8b2 commit ff233a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3420
-206
lines changed

src/compiler/checker.ts

+97-64
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -1706,11 +1706,7 @@
17061706
"category": "Error",
17071707
"code": 2378
17081708
},
1709-
"Getter and setter accessors do not agree in visibility.": {
1710-
"category": "Error",
1711-
"code": 2379
1712-
},
1713-
"'get' and 'set' accessor must have the same type.": {
1709+
"The return type of a 'get' accessor must be assignable to its 'set' accessor type": {
17141710
"category": "Error",
17151711
"code": 2380
17161712
},
@@ -3308,6 +3304,10 @@
33083304
"category": "Error",
33093305
"code": 2807
33103306
},
3307+
"A get accessor must be at least as accessible as the setter": {
3308+
"category": "Error",
3309+
"code": 2808
3310+
},
33113311

33123312
"Import declaration '{0}' is using private name '{1}'.": {
33133313
"category": "Error",

src/compiler/parser.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -3213,7 +3213,10 @@ namespace ts {
32133213

32143214
function isTypeMemberStart(): boolean {
32153215
// Return true if we have the start of a signature member
3216-
if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) {
3216+
if (token() === SyntaxKind.OpenParenToken ||
3217+
token() === SyntaxKind.LessThanToken ||
3218+
token() === SyntaxKind.GetKeyword ||
3219+
token() === SyntaxKind.SetKeyword) {
32173220
return true;
32183221
}
32193222
let idToken = false;
@@ -3254,6 +3257,14 @@ namespace ts {
32543257
const pos = getNodePos();
32553258
const hasJSDoc = hasPrecedingJSDocComment();
32563259
const modifiers = parseModifiers();
3260+
if (parseContextualModifier(SyntaxKind.GetKeyword)) {
3261+
return parseAccessorDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.GetAccessor);
3262+
}
3263+
3264+
if (parseContextualModifier(SyntaxKind.SetKeyword)) {
3265+
return parseAccessorDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.SetAccessor);
3266+
}
3267+
32573268
if (isIndexSignature()) {
32583269
return parseIndexSignatureDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers);
32593270
}

src/compiler/types.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1482,19 +1482,19 @@ namespace ts {
14821482

14831483
// See the comment on MethodDeclaration for the intuition behind GetAccessorDeclaration being a
14841484
// ClassElement and an ObjectLiteralElement.
1485-
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
1485+
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
14861486
readonly kind: SyntaxKind.GetAccessor;
1487-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
1487+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
14881488
readonly name: PropertyName;
14891489
readonly body?: FunctionBody;
14901490
/* @internal */ typeParameters?: NodeArray<TypeParameterDeclaration>; // Present for use with reporting a grammar error
14911491
}
14921492

14931493
// See the comment on MethodDeclaration for the intuition behind SetAccessorDeclaration being a
14941494
// ClassElement and an ObjectLiteralElement.
1495-
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
1495+
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
14961496
readonly kind: SyntaxKind.SetAccessor;
1497-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
1497+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
14981498
readonly name: PropertyName;
14991499
readonly body?: FunctionBody;
15001500
/* @internal */ typeParameters?: NodeArray<TypeParameterDeclaration>; // Present for use with reporting a grammar error
@@ -4776,6 +4776,7 @@ namespace ts {
47764776
immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead.
47774777
target?: Symbol; // Resolved (non-alias) target of an alias
47784778
type?: Type; // Type of value symbol
4779+
writeType?: Type; // Type of value symbol in write contexts
47794780
nameType?: Type; // Type associated with a late-bound symbol
47804781
uniqueESSymbolType?: Type; // UniqueESSymbol type for a symbol
47814782
declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter

src/compiler/utilities.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -5306,9 +5306,10 @@ namespace ts {
53065306
return symbol.flags & SymbolFlags.Transient ? (<TransientSymbol>symbol).checkFlags : 0;
53075307
}
53085308

5309-
export function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags {
5309+
export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false): ModifierFlags {
53105310
if (s.valueDeclaration) {
5311-
const flags = getCombinedModifierFlags(s.valueDeclaration);
5311+
const declaration = (isWrite && s.declarations && find(s.declarations, d => d.kind === SyntaxKind.SetAccessor)) || s.valueDeclaration;
5312+
const flags = getCombinedModifierFlags(declaration);
53125313
return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier;
53135314
}
53145315
if (getCheckFlags(s) & CheckFlags.Synthetic) {

tests/baselines/reference/abstractPropertyNegative.errors.txt

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
tests/cases/compiler/abstractPropertyNegative.ts(10,18): error TS2380: 'get' and 'set' accessor must have the same type.
2-
tests/cases/compiler/abstractPropertyNegative.ts(11,18): error TS2380: 'get' and 'set' accessor must have the same type.
1+
tests/cases/compiler/abstractPropertyNegative.ts(10,18): error TS2380: The return type of a 'get' accessor must be assignable to its 'set' accessor type
32
tests/cases/compiler/abstractPropertyNegative.ts(13,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'm' from class 'B'.
43
tests/cases/compiler/abstractPropertyNegative.ts(13,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'mismatch' from class 'B'.
54
tests/cases/compiler/abstractPropertyNegative.ts(13,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'prop' from class 'B'.
@@ -19,7 +18,7 @@ tests/cases/compiler/abstractPropertyNegative.ts(40,9): error TS2676: Accessors
1918
tests/cases/compiler/abstractPropertyNegative.ts(41,18): error TS2676: Accessors must both be abstract or non-abstract.
2019

2120

22-
==== tests/cases/compiler/abstractPropertyNegative.ts (16 errors) ====
21+
==== tests/cases/compiler/abstractPropertyNegative.ts (15 errors) ====
2322
interface A {
2423
prop: string;
2524
m(): string;
@@ -31,10 +30,8 @@ tests/cases/compiler/abstractPropertyNegative.ts(41,18): error TS2676: Accessors
3130
abstract m(): string;
3231
abstract get mismatch(): string;
3332
~~~~~~~~
34-
!!! error TS2380: 'get' and 'set' accessor must have the same type.
33+
!!! error TS2380: The return type of a 'get' accessor must be assignable to its 'set' accessor type
3534
abstract set mismatch(val: number); // error, not same type
36-
~~~~~~~~
37-
!!! error TS2380: 'get' and 'set' accessor must have the same type.
3835
}
3936
class C extends B {
4037
~
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
tests/cases/compiler/accessorBodyInTypeContext.ts(2,15): error TS1183: An implementation cannot be declared in ambient contexts.
2+
tests/cases/compiler/accessorBodyInTypeContext.ts(6,21): error TS1183: An implementation cannot be declared in ambient contexts.
3+
tests/cases/compiler/accessorBodyInTypeContext.ts(10,15): error TS1183: An implementation cannot be declared in ambient contexts.
4+
tests/cases/compiler/accessorBodyInTypeContext.ts(14,21): error TS1183: An implementation cannot be declared in ambient contexts.
5+
6+
7+
==== tests/cases/compiler/accessorBodyInTypeContext.ts (4 errors) ====
8+
type A = {
9+
get foo() { return 0 }
10+
~~~~~~~~~~~~
11+
!!! error TS1183: An implementation cannot be declared in ambient contexts.
12+
};
13+
14+
type B = {
15+
set foo(v: any) { }
16+
~~~
17+
!!! error TS1183: An implementation cannot be declared in ambient contexts.
18+
};
19+
20+
interface X {
21+
get foo() { return 0 }
22+
~~~~~~~~~~~~
23+
!!! error TS1183: An implementation cannot be declared in ambient contexts.
24+
}
25+
26+
interface Y {
27+
set foo(v: any) { }
28+
~~~
29+
!!! error TS1183: An implementation cannot be declared in ambient contexts.
30+
}
31+
32+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [accessorBodyInTypeContext.ts]
2+
type A = {
3+
get foo() { return 0 }
4+
};
5+
6+
type B = {
7+
set foo(v: any) { }
8+
};
9+
10+
interface X {
11+
get foo() { return 0 }
12+
}
13+
14+
interface Y {
15+
set foo(v: any) { }
16+
}
17+
18+
19+
20+
//// [accessorBodyInTypeContext.js]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/compiler/accessorBodyInTypeContext.ts ===
2+
type A = {
3+
>A : Symbol(A, Decl(accessorBodyInTypeContext.ts, 0, 0))
4+
5+
get foo() { return 0 }
6+
>foo : Symbol(foo, Decl(accessorBodyInTypeContext.ts, 0, 10))
7+
8+
};
9+
10+
type B = {
11+
>B : Symbol(B, Decl(accessorBodyInTypeContext.ts, 2, 2))
12+
13+
set foo(v: any) { }
14+
>foo : Symbol(foo, Decl(accessorBodyInTypeContext.ts, 4, 10))
15+
>v : Symbol(v, Decl(accessorBodyInTypeContext.ts, 5, 12))
16+
17+
};
18+
19+
interface X {
20+
>X : Symbol(X, Decl(accessorBodyInTypeContext.ts, 6, 2))
21+
22+
get foo() { return 0 }
23+
>foo : Symbol(X.foo, Decl(accessorBodyInTypeContext.ts, 8, 13))
24+
}
25+
26+
interface Y {
27+
>Y : Symbol(Y, Decl(accessorBodyInTypeContext.ts, 10, 1))
28+
29+
set foo(v: any) { }
30+
>foo : Symbol(Y.foo, Decl(accessorBodyInTypeContext.ts, 12, 13))
31+
>v : Symbol(v, Decl(accessorBodyInTypeContext.ts, 13, 12))
32+
}
33+
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== tests/cases/compiler/accessorBodyInTypeContext.ts ===
2+
type A = {
3+
>A : A
4+
5+
get foo() { return 0 }
6+
>foo : number
7+
>0 : 0
8+
9+
};
10+
11+
type B = {
12+
>B : B
13+
14+
set foo(v: any) { }
15+
>foo : any
16+
>v : any
17+
18+
};
19+
20+
interface X {
21+
get foo() { return 0 }
22+
>foo : number
23+
>0 : 0
24+
}
25+
26+
interface Y {
27+
set foo(v: any) { }
28+
>foo : any
29+
>v : any
30+
}
31+
32+

tests/baselines/reference/accessorWithMismatchedAccessibilityModifiers.errors.txt

-58
This file was deleted.

tests/baselines/reference/api/tsserverlibrary.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -827,15 +827,15 @@ declare namespace ts {
827827
readonly kind: SyntaxKind.SemicolonClassElement;
828828
readonly parent: ClassLikeDeclaration;
829829
}
830-
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
830+
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
831831
readonly kind: SyntaxKind.GetAccessor;
832-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
832+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
833833
readonly name: PropertyName;
834834
readonly body?: FunctionBody;
835835
}
836-
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
836+
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
837837
readonly kind: SyntaxKind.SetAccessor;
838-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
838+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
839839
readonly name: PropertyName;
840840
readonly body?: FunctionBody;
841841
}

tests/baselines/reference/api/typescript.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -827,15 +827,15 @@ declare namespace ts {
827827
readonly kind: SyntaxKind.SemicolonClassElement;
828828
readonly parent: ClassLikeDeclaration;
829829
}
830-
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
830+
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
831831
readonly kind: SyntaxKind.GetAccessor;
832-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
832+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
833833
readonly name: PropertyName;
834834
readonly body?: FunctionBody;
835835
}
836-
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
836+
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
837837
readonly kind: SyntaxKind.SetAccessor;
838-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
838+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
839839
readonly name: PropertyName;
840840
readonly body?: FunctionBody;
841841
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//// [divergentAccessors1.ts]
2+
// Accessors in interfaces/types
3+
4+
{
5+
interface IHasGetSet {
6+
get foo(): number;
7+
set foo(v: number | string);
8+
}
9+
10+
const ihgs: IHasGetSet = null as any;
11+
ihgs.foo = "32";
12+
let r_ihgs_foo: number = ihgs.foo;
13+
}
14+
15+
{
16+
type T_HasGetSet = {
17+
get foo(): number;
18+
set foo(v: number | string);
19+
}
20+
21+
const t_hgs: T_HasGetSet = null as any;
22+
t_hgs.foo = "32";
23+
let r_t_hgs_foo: number = t_hgs.foo;
24+
}
25+
26+
27+
//// [divergentAccessors1.js]
28+
"use strict";
29+
// Accessors in interfaces/types
30+
{
31+
var ihgs = null;
32+
ihgs.foo = "32";
33+
var r_ihgs_foo = ihgs.foo;
34+
}
35+
{
36+
var t_hgs = null;
37+
t_hgs.foo = "32";
38+
var r_t_hgs_foo = t_hgs.foo;
39+
}

0 commit comments

Comments
 (0)