Skip to content

Commit

Permalink
Variant accessors (#42425)
Browse files Browse the repository at this point in the history
Allows accessors to have different access modifiers and types

Fixes #2845, #2521
  • Loading branch information
RyanCavanaugh authored Mar 27, 2021
1 parent 2f0c8b2 commit ff233a9
Show file tree
Hide file tree
Showing 43 changed files with 3,420 additions and 206 deletions.
161 changes: 97 additions & 64 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1706,11 +1706,7 @@
"category": "Error",
"code": 2378
},
"Getter and setter accessors do not agree in visibility.": {
"category": "Error",
"code": 2379
},
"'get' and 'set' accessor must have the same type.": {
"The return type of a 'get' accessor must be assignable to its 'set' accessor type": {
"category": "Error",
"code": 2380
},
Expand Down Expand Up @@ -3308,6 +3304,10 @@
"category": "Error",
"code": 2807
},
"A get accessor must be at least as accessible as the setter": {
"category": "Error",
"code": 2808
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
13 changes: 12 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3213,7 +3213,10 @@ namespace ts {

function isTypeMemberStart(): boolean {
// Return true if we have the start of a signature member
if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) {
if (token() === SyntaxKind.OpenParenToken ||
token() === SyntaxKind.LessThanToken ||
token() === SyntaxKind.GetKeyword ||
token() === SyntaxKind.SetKeyword) {
return true;
}
let idToken = false;
Expand Down Expand Up @@ -3254,6 +3257,14 @@ namespace ts {
const pos = getNodePos();
const hasJSDoc = hasPrecedingJSDocComment();
const modifiers = parseModifiers();
if (parseContextualModifier(SyntaxKind.GetKeyword)) {
return parseAccessorDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.GetAccessor);
}

if (parseContextualModifier(SyntaxKind.SetKeyword)) {
return parseAccessorDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.SetAccessor);
}

if (isIndexSignature()) {
return parseIndexSignatureDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers);
}
Expand Down
9 changes: 5 additions & 4 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1482,19 +1482,19 @@ namespace ts {

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

// See the comment on MethodDeclaration for the intuition behind SetAccessorDeclaration being a
// ClassElement and an ObjectLiteralElement.
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
readonly kind: SyntaxKind.SetAccessor;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
readonly name: PropertyName;
readonly body?: FunctionBody;
/* @internal */ typeParameters?: NodeArray<TypeParameterDeclaration>; // Present for use with reporting a grammar error
Expand Down Expand Up @@ -4776,6 +4776,7 @@ namespace ts {
immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead.
target?: Symbol; // Resolved (non-alias) target of an alias
type?: Type; // Type of value symbol
writeType?: Type; // Type of value symbol in write contexts
nameType?: Type; // Type associated with a late-bound symbol
uniqueESSymbolType?: Type; // UniqueESSymbol type for a symbol
declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5306,9 +5306,10 @@ namespace ts {
return symbol.flags & SymbolFlags.Transient ? (<TransientSymbol>symbol).checkFlags : 0;
}

export function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags {
export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false): ModifierFlags {
if (s.valueDeclaration) {
const flags = getCombinedModifierFlags(s.valueDeclaration);
const declaration = (isWrite && s.declarations && find(s.declarations, d => d.kind === SyntaxKind.SetAccessor)) || s.valueDeclaration;
const flags = getCombinedModifierFlags(declaration);
return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier;
}
if (getCheckFlags(s) & CheckFlags.Synthetic) {
Expand Down
9 changes: 3 additions & 6 deletions tests/baselines/reference/abstractPropertyNegative.errors.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
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(10,18): error TS2380: The return type of a 'get' accessor must be assignable to its 'set' accessor 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'.
Expand All @@ -19,7 +18,7 @@ tests/cases/compiler/abstractPropertyNegative.ts(40,9): error TS2676: Accessors
tests/cases/compiler/abstractPropertyNegative.ts(41,18): error TS2676: Accessors must both be abstract or non-abstract.


==== tests/cases/compiler/abstractPropertyNegative.ts (16 errors) ====
==== tests/cases/compiler/abstractPropertyNegative.ts (15 errors) ====
interface A {
prop: string;
m(): string;
Expand All @@ -31,10 +30,8 @@ tests/cases/compiler/abstractPropertyNegative.ts(41,18): error TS2676: Accessors
abstract m(): string;
abstract get mismatch(): string;
~~~~~~~~
!!! error TS2380: 'get' and 'set' accessor must have the same type.
!!! error TS2380: The return type of a 'get' accessor must be assignable to its 'set' accessor 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 {
~
Expand Down
32 changes: 32 additions & 0 deletions tests/baselines/reference/accessorBodyInTypeContext.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
tests/cases/compiler/accessorBodyInTypeContext.ts(2,15): error TS1183: An implementation cannot be declared in ambient contexts.
tests/cases/compiler/accessorBodyInTypeContext.ts(6,21): error TS1183: An implementation cannot be declared in ambient contexts.
tests/cases/compiler/accessorBodyInTypeContext.ts(10,15): error TS1183: An implementation cannot be declared in ambient contexts.
tests/cases/compiler/accessorBodyInTypeContext.ts(14,21): error TS1183: An implementation cannot be declared in ambient contexts.


==== tests/cases/compiler/accessorBodyInTypeContext.ts (4 errors) ====
type A = {
get foo() { return 0 }
~~~~~~~~~~~~
!!! error TS1183: An implementation cannot be declared in ambient contexts.
};

type B = {
set foo(v: any) { }
~~~
!!! error TS1183: An implementation cannot be declared in ambient contexts.
};

interface X {
get foo() { return 0 }
~~~~~~~~~~~~
!!! error TS1183: An implementation cannot be declared in ambient contexts.
}

interface Y {
set foo(v: any) { }
~~~
!!! error TS1183: An implementation cannot be declared in ambient contexts.
}


20 changes: 20 additions & 0 deletions tests/baselines/reference/accessorBodyInTypeContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//// [accessorBodyInTypeContext.ts]
type A = {
get foo() { return 0 }
};

type B = {
set foo(v: any) { }
};

interface X {
get foo() { return 0 }
}

interface Y {
set foo(v: any) { }
}



//// [accessorBodyInTypeContext.js]
34 changes: 34 additions & 0 deletions tests/baselines/reference/accessorBodyInTypeContext.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
=== tests/cases/compiler/accessorBodyInTypeContext.ts ===
type A = {
>A : Symbol(A, Decl(accessorBodyInTypeContext.ts, 0, 0))

get foo() { return 0 }
>foo : Symbol(foo, Decl(accessorBodyInTypeContext.ts, 0, 10))

};

type B = {
>B : Symbol(B, Decl(accessorBodyInTypeContext.ts, 2, 2))

set foo(v: any) { }
>foo : Symbol(foo, Decl(accessorBodyInTypeContext.ts, 4, 10))
>v : Symbol(v, Decl(accessorBodyInTypeContext.ts, 5, 12))

};

interface X {
>X : Symbol(X, Decl(accessorBodyInTypeContext.ts, 6, 2))

get foo() { return 0 }
>foo : Symbol(X.foo, Decl(accessorBodyInTypeContext.ts, 8, 13))
}

interface Y {
>Y : Symbol(Y, Decl(accessorBodyInTypeContext.ts, 10, 1))

set foo(v: any) { }
>foo : Symbol(Y.foo, Decl(accessorBodyInTypeContext.ts, 12, 13))
>v : Symbol(v, Decl(accessorBodyInTypeContext.ts, 13, 12))
}


32 changes: 32 additions & 0 deletions tests/baselines/reference/accessorBodyInTypeContext.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
=== tests/cases/compiler/accessorBodyInTypeContext.ts ===
type A = {
>A : A

get foo() { return 0 }
>foo : number
>0 : 0

};

type B = {
>B : B

set foo(v: any) { }
>foo : any
>v : any

};

interface X {
get foo() { return 0 }
>foo : number
>0 : 0
}

interface Y {
set foo(v: any) { }
>foo : any
>v : any
}


This file was deleted.

8 changes: 4 additions & 4 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,15 +827,15 @@ declare namespace ts {
readonly kind: SyntaxKind.SemicolonClassElement;
readonly parent: ClassLikeDeclaration;
}
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
readonly kind: SyntaxKind.GetAccessor;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
readonly name: PropertyName;
readonly body?: FunctionBody;
}
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
readonly kind: SyntaxKind.SetAccessor;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
readonly name: PropertyName;
readonly body?: FunctionBody;
}
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,15 +827,15 @@ declare namespace ts {
readonly kind: SyntaxKind.SemicolonClassElement;
readonly parent: ClassLikeDeclaration;
}
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
readonly kind: SyntaxKind.GetAccessor;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
readonly name: PropertyName;
readonly body?: FunctionBody;
}
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
readonly kind: SyntaxKind.SetAccessor;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
readonly name: PropertyName;
readonly body?: FunctionBody;
}
Expand Down
39 changes: 39 additions & 0 deletions tests/baselines/reference/divergentAccessors1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//// [divergentAccessors1.ts]
// Accessors in interfaces/types

{
interface IHasGetSet {
get foo(): number;
set foo(v: number | string);
}

const ihgs: IHasGetSet = null as any;
ihgs.foo = "32";
let r_ihgs_foo: number = ihgs.foo;
}

{
type T_HasGetSet = {
get foo(): number;
set foo(v: number | string);
}

const t_hgs: T_HasGetSet = null as any;
t_hgs.foo = "32";
let r_t_hgs_foo: number = t_hgs.foo;
}


//// [divergentAccessors1.js]
"use strict";
// Accessors in interfaces/types
{
var ihgs = null;
ihgs.foo = "32";
var r_ihgs_foo = ihgs.foo;
}
{
var t_hgs = null;
t_hgs.foo = "32";
var r_t_hgs_foo = t_hgs.foo;
}
Loading

0 comments on commit ff233a9

Please sign in to comment.