Skip to content

Commit 6f09705

Browse files
committed
Divergent get/set types and visibility
1 parent d33143c commit 6f09705

Some content is hidden

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

41 files changed

+3164
-157
lines changed

src/compiler/checker.ts

+37-15
Original file line numberDiff line numberDiff line change
@@ -25967,10 +25967,18 @@ namespace ts {
2596725967
*/
2596825968
function checkPropertyAccessibility(
2596925969
node: PropertyAccessExpression | QualifiedName | PropertyAccessExpression | VariableDeclaration | ParameterDeclaration | ImportTypeNode | PropertyAssignment | ShorthandPropertyAssignment | BindingElement,
25970-
isSuper: boolean, type: Type, prop: Symbol): boolean {
25971-
const flags = getDeclarationModifierFlagsFromSymbol(prop);
25970+
isSuper: boolean, type: Type, prop: Symbol, isWrite = false): boolean {
25971+
let flags = getDeclarationModifierFlagsFromSymbol(prop);
2597225972
const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right : node.kind === SyntaxKind.ImportType ? node : node.name;
2597325973

25974+
// Writes use the visibility modifier of the set accessor, if one is declared
25975+
if (isWrite) {
25976+
const setter = forEach(prop.declarations, p => p.kind === SyntaxKind.SetAccessor && p);
25977+
if (setter) {
25978+
flags = (flags & ~ModifierFlags.AccessibilityModifier) | getEffectiveDeclarationFlags(setter, ModifierFlags.AccessibilityModifier);
25979+
}
25980+
}
25981+
2597425982
if (isSuper) {
2597525983
// TS 1.0 spec (April 2014): 4.8.2
2597625984
// - In a constructor, instance member function, instance member accessor, or
@@ -26285,7 +26293,7 @@ namespace ts {
2628526293
markAliasReferenced(parentSymbol, node);
2628626294
}
2628726295

26288-
let propType: Type;
26296+
let propType: Type | undefined;
2628926297
if (!prop) {
2629026298
const indexInfo = !isPrivateIdentifier(right) && (assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType)) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined;
2629126299
if (!(indexInfo && indexInfo.type)) {
@@ -26316,19 +26324,30 @@ namespace ts {
2631626324
}
2631726325
}
2631826326
else {
26327+
const isWrite = isWriteAccess(node);
2631926328
if (getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) {
2632026329
errorOrSuggestion(/* isError */ false, right, Diagnostics._0_is_deprecated, right.escapedText as string);
2632126330
}
2632226331
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
2632326332
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
2632426333
getNodeLinks(node).resolvedSymbol = prop;
26325-
checkPropertyAccessibility(node, left.kind === SyntaxKind.SuperKeyword, apparentType, prop);
26334+
checkPropertyAccessibility(node, left.kind === SyntaxKind.SuperKeyword, apparentType, prop, isWrite);
2632626335
if (isAssignmentToReadonlyEntity(node as Expression, prop, assignmentKind)) {
2632726336
error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, idText(right));
2632826337
return errorType;
2632926338
}
26330-
propType = isThisPropertyAccessInConstructor(node, prop) ? autoType : getConstraintForLocation(getTypeOfSymbol(prop), node);
26339+
if (isWrite) {
26340+
const setter = forEach(prop.declarations, p => (p.kind === SyntaxKind.SetAccessor && p));
26341+
if (setter) {
26342+
propType = getAnnotatedAccessorType(setter as SetAccessorDeclaration);
26343+
}
26344+
}
26345+
26346+
if (propType === undefined) {
26347+
propType = isThisPropertyAccessInConstructor(node, prop) ? autoType : getConstraintForLocation(getTypeOfSymbol(prop), node);
26348+
}
2633126349
}
26350+
// For writes to properties declared as accessors, use the 'set' type
2633226351
return getFlowTypeOfAccessExpression(node, prop, propType, right);
2633326352
}
2633426353

@@ -32321,15 +32340,19 @@ namespace ts {
3232132340
const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
3232232341
const otherAccessor = getDeclarationOfKind<AccessorDeclaration>(getSymbolOfNode(node), otherKind);
3232332342
if (otherAccessor) {
32324-
const nodeFlags = getEffectiveModifierFlags(node);
32325-
const otherFlags = getEffectiveModifierFlags(otherAccessor);
32326-
if ((nodeFlags & ModifierFlags.Abstract) !== (otherFlags & ModifierFlags.Abstract)) {
32343+
const getter = node.kind === SyntaxKind.GetAccessor ? node : otherAccessor;
32344+
const setter = node.kind === SyntaxKind.SetAccessor ? node : otherAccessor;
32345+
const getterFlags = getEffectiveModifierFlags(getter);
32346+
const setterFlags = getEffectiveModifierFlags(setter);
32347+
if ((getterFlags & ModifierFlags.Abstract) !== (setterFlags & ModifierFlags.Abstract)) {
3232732348
error(node.name, Diagnostics.Accessors_must_both_be_abstract_or_non_abstract);
3232832349
}
32350+
if (((getterFlags & ModifierFlags.Protected) && !(setterFlags & (ModifierFlags.Protected | ModifierFlags.Private))) ||
32351+
((getterFlags & ModifierFlags.Private) && !(setterFlags & ModifierFlags.Private))) {
32352+
error(node.name, Diagnostics.A_get_accessor_must_be_at_least_as_accessible_as_the_setter);
32353+
}
3232932354

32330-
// TypeScript 1.0 spec (April 2014): 4.5
32331-
// If both accessors include type annotations, the specified types must be identical.
32332-
checkAccessorDeclarationTypesAssignable(node, otherAccessor, getAnnotatedAccessorType, Diagnostics.get_and_set_accessor_must_have_the_comparable_types);
32355+
checkAccessorDeclarationTypesAssignable(getter, setter, getAnnotatedAccessorType, Diagnostics.The_return_type_of_a_get_accessor_must_be_assignable_to_its_set_accessor_type);
3233332356
checkAccessorDeclarationTypesIdentical(node, otherAccessor, getThisTypeOfDeclaration, Diagnostics.get_and_set_accessor_must_have_the_same_this_type);
3233432357
}
3233532358
}
@@ -32345,8 +32368,8 @@ namespace ts {
3234532368
return checkAccessorDeclarationTypesMatch(first, second, getAnnotatedType, isTypeIdenticalTo, message);
3234632369
}
3234732370

32348-
function checkAccessorDeclarationTypesAssignable(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type | undefined, message: DiagnosticMessage) {
32349-
return checkAccessorDeclarationTypesMatch(first, second, getAnnotatedType, areTypesComparable, message);
32371+
function checkAccessorDeclarationTypesAssignable(getter: AccessorDeclaration, setter: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type | undefined, message: DiagnosticMessage) {
32372+
return checkAccessorDeclarationTypesMatch(getter, setter, getAnnotatedType, isTypeAssignableTo, message);
3235032373
}
3235132374

3235232375
function checkAccessorDeclarationTypesMatch(first: AccessorDeclaration, second: AccessorDeclaration, getAnnotatedType: (a: AccessorDeclaration) => Type | undefined, match: typeof areTypesComparable, message: DiagnosticMessage) {
@@ -32357,7 +32380,6 @@ namespace ts {
3235732380
}
3235832381
}
3235932382

32360-
3236132383
function checkMissingDeclaration(node: Node) {
3236232384
checkDecorators(node);
3236332385
}
@@ -40171,7 +40193,7 @@ namespace ts {
4017140193
}
4017240194

4017340195
function checkGrammarAccessor(accessor: AccessorDeclaration): boolean {
40174-
if (!(accessor.flags & NodeFlags.Ambient)) {
40196+
if (!(accessor.flags & NodeFlags.Ambient) && (accessor.parent.kind !== SyntaxKind.TypeLiteral) && (accessor.parent.kind !== SyntaxKind.InterfaceDeclaration)) {
4017540197
if (languageVersion < ScriptTarget.ES5) {
4017640198
return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher);
4017740199
}

src/compiler/diagnosticMessages.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -1697,7 +1697,7 @@
16971697
"category": "Error",
16981698
"code": 2378
16991699
},
1700-
"'get' and 'set' accessor must have comparable types.": {
1700+
"The return type of a 'get' accessor must be assignable to its 'set' accessor type": {
17011701
"category": "Error",
17021702
"code": 2380
17031703
},
@@ -3231,6 +3231,10 @@
32313231
"category": "Error",
32323232
"code": 2797
32333233
},
3234+
"A get accessor must be at least as accessible as the setter": {
3235+
"category": "Error",
3236+
"code": 2798
3237+
},
32343238

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

src/compiler/parser.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,8 @@ namespace ts {
13051305
}
13061306

13071307
function parseErrorAtPosition(start: number, length: number, message: DiagnosticMessage, arg0?: any): void {
1308+
debugger;
1309+
13081310
// Don't report another error if it would just be at the same position as the last error.
13091311
const lastError = lastOrUndefined(parseDiagnostics);
13101312
if (!lastError || start !== lastError.start) {
@@ -3172,7 +3174,10 @@ namespace ts {
31723174

31733175
function isTypeMemberStart(): boolean {
31743176
// Return true if we have the start of a signature member
3175-
if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) {
3177+
if (token() === SyntaxKind.OpenParenToken ||
3178+
token() === SyntaxKind.LessThanToken ||
3179+
token() === SyntaxKind.GetKeyword ||
3180+
token() === SyntaxKind.SetKeyword) {
31763181
return true;
31773182
}
31783183
let idToken = false;
@@ -3213,6 +3218,14 @@ namespace ts {
32133218
const pos = getNodePos();
32143219
const hasJSDoc = hasPrecedingJSDocComment();
32153220
const modifiers = parseModifiers();
3221+
if (parseContextualModifier(SyntaxKind.GetKeyword)) {
3222+
return parseAccessorDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.GetAccessor);
3223+
}
3224+
3225+
if (parseContextualModifier(SyntaxKind.SetKeyword)) {
3226+
return parseAccessorDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.SetAccessor);
3227+
}
3228+
32163229
if (isIndexSignature()) {
32173230
return parseIndexSignatureDeclaration(pos, hasJSDoc, /*decorators*/ undefined, modifiers);
32183231
}

src/compiler/types.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1453,19 +1453,19 @@ namespace ts {
14531453

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

14641464
// See the comment on MethodDeclaration for the intuition behind SetAccessorDeclaration being a
14651465
// ClassElement and an ObjectLiteralElement.
1466-
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
1466+
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
14671467
readonly kind: SyntaxKind.SetAccessor;
1468-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
1468+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
14691469
readonly name: PropertyName;
14701470
readonly body?: FunctionBody;
14711471
/* @internal */ typeParameters?: NodeArray<TypeParameterDeclaration>; // Present for use with reporting a grammar error

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
~

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
@@ -820,15 +820,15 @@ declare namespace ts {
820820
readonly kind: SyntaxKind.SemicolonClassElement;
821821
readonly parent: ClassLikeDeclaration;
822822
}
823-
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
823+
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
824824
readonly kind: SyntaxKind.GetAccessor;
825-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
825+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
826826
readonly name: PropertyName;
827827
readonly body?: FunctionBody;
828828
}
829-
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
829+
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
830830
readonly kind: SyntaxKind.SetAccessor;
831-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
831+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
832832
readonly name: PropertyName;
833833
readonly body?: FunctionBody;
834834
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -820,15 +820,15 @@ declare namespace ts {
820820
readonly kind: SyntaxKind.SemicolonClassElement;
821821
readonly parent: ClassLikeDeclaration;
822822
}
823-
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
823+
export interface GetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
824824
readonly kind: SyntaxKind.GetAccessor;
825-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
825+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
826826
readonly name: PropertyName;
827827
readonly body?: FunctionBody;
828828
}
829-
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
829+
export interface SetAccessorDeclaration extends FunctionLikeDeclarationBase, ClassElement, TypeElement, ObjectLiteralElement, JSDocContainer {
830830
readonly kind: SyntaxKind.SetAccessor;
831-
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression;
831+
readonly parent: ClassLikeDeclaration | ObjectLiteralExpression | TypeLiteralNode | InterfaceDeclaration;
832832
readonly name: PropertyName;
833833
readonly body?: FunctionBody;
834834
}

0 commit comments

Comments
 (0)