Skip to content

Commit 7806de0

Browse files
author
Andy
committed
Merge pull request #8555 from Microsoft/readonly_ctr
Fix #7590: Allow 'readonly' to be used in constructor parameters
2 parents eb2845d + 22ee90a commit 7806de0

23 files changed

+240
-22
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12680,7 +12680,7 @@ namespace ts {
1268012680

1268112681
checkVariableLikeDeclaration(node);
1268212682
let func = getContainingFunction(node);
12683-
if (node.flags & NodeFlags.AccessibilityModifier) {
12683+
if (node.flags & NodeFlags.ParameterPropertyModifier) {
1268412684
func = getContainingFunction(node);
1268512685
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
1268612686
error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
@@ -13016,7 +13016,7 @@ namespace ts {
1301613016
// or the containing class declares instance member variables with initializers.
1301713017
const superCallShouldBeFirst =
1301813018
forEach((<ClassDeclaration>node.parent).members, isInstancePropertyWithInitializer) ||
13019-
forEach(node.parameters, p => p.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected));
13019+
forEach(node.parameters, p => p.flags & NodeFlags.ParameterPropertyModifier);
1302013020

1302113021
// Skip past any prologue directives to find the first statement
1302213022
// to ensure that it was a super call.
@@ -17673,7 +17673,8 @@ namespace ts {
1767317673
if (flags & NodeFlags.Readonly) {
1767417674
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly");
1767517675
}
17676-
else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature) {
17676+
else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.Parameter) {
17677+
// If node.kind === SyntaxKind.Parameter, checkParameter report an error if it's not a parameter property.
1767717678
return grammarErrorOnNode(modifier, Diagnostics.readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature);
1767817679
}
1767917680
flags |= NodeFlags.Readonly;
@@ -17781,7 +17782,7 @@ namespace ts {
1778117782
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & NodeFlags.Ambient) {
1778217783
return grammarErrorOnNode(lastDeclare, Diagnostics.A_0_modifier_cannot_be_used_with_an_import_declaration, "declare");
1778317784
}
17784-
else if (node.kind === SyntaxKind.Parameter && (flags & NodeFlags.AccessibilityModifier) && isBindingPattern((<ParameterDeclaration>node).name)) {
17785+
else if (node.kind === SyntaxKind.Parameter && (flags & NodeFlags.ParameterPropertyModifier) && isBindingPattern((<ParameterDeclaration>node).name)) {
1778517786
return grammarErrorOnNode(node, Diagnostics.A_parameter_property_may_not_be_a_binding_pattern);
1778617787
}
1778717788
if (flags & NodeFlags.Async) {
@@ -18246,9 +18247,6 @@ namespace ts {
1824618247
if (parameter.dotDotDotToken) {
1824718248
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_set_accessor_cannot_have_rest_parameter);
1824818249
}
18249-
else if (parameter.flags & NodeFlags.Modifier) {
18250-
return grammarErrorOnNode(accessor.name, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
18251-
}
1825218250
else if (parameter.questionToken) {
1825318251
return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_set_accessor_cannot_have_an_optional_parameter);
1825418252
}

src/compiler/declarationEmitter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ namespace ts {
10511051
function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) {
10521052
if (constructorDeclaration) {
10531053
forEach(constructorDeclaration.parameters, param => {
1054-
if (param.flags & NodeFlags.AccessibilityModifier) {
1054+
if (param.flags & NodeFlags.ParameterPropertyModifier) {
10551055
emitPropertyDeclaration(param);
10561056
}
10571057
});

src/compiler/emitter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4979,7 +4979,7 @@ const _super = (function (geti, seti) {
49794979

49804980
function emitParameterPropertyAssignments(node: ConstructorDeclaration) {
49814981
forEach(node.parameters, param => {
4982-
if (param.flags & NodeFlags.AccessibilityModifier) {
4982+
if (param.flags & NodeFlags.ParameterPropertyModifier) {
49834983
writeLine();
49844984
emitStart(param);
49854985
emitStart(param.name);

src/compiler/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,10 @@ namespace ts {
407407
HasAggregatedChildData = 1 << 29, // If we've computed data from children and cached it in this node
408408
HasJsxSpreadAttribute = 1 << 30,
409409

410-
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async,
410+
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async | Readonly,
411411
AccessibilityModifier = Public | Private | Protected,
412+
// Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property.
413+
ParameterPropertyModifier = AccessibilityModifier | Readonly,
412414
BlockScoped = Let | Const,
413415

414416
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,

src/compiler/utilities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3040,7 +3040,7 @@ namespace ts {
30403040
}
30413041

30423042
export function isParameterPropertyDeclaration(node: ParameterDeclaration): boolean {
3043-
return node.flags & NodeFlags.AccessibilityModifier && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
3043+
return node.flags & NodeFlags.ParameterPropertyModifier && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
30443044
}
30453045

30463046
export function startsWith(str: string, prefix: string): boolean {
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
1-
tests/cases/compiler/accessorParameterAccessibilityModifier.ts(3,9): error TS2369: A parameter property is only allowed in a constructor implementation.
21
tests/cases/compiler/accessorParameterAccessibilityModifier.ts(3,11): error TS2369: A parameter property is only allowed in a constructor implementation.
3-
tests/cases/compiler/accessorParameterAccessibilityModifier.ts(4,16): error TS2369: A parameter property is only allowed in a constructor implementation.
42
tests/cases/compiler/accessorParameterAccessibilityModifier.ts(4,18): error TS2369: A parameter property is only allowed in a constructor implementation.
53

64

7-
==== tests/cases/compiler/accessorParameterAccessibilityModifier.ts (4 errors) ====
5+
==== tests/cases/compiler/accessorParameterAccessibilityModifier.ts (2 errors) ====
86

97
class C {
108
set X(public v) { }
11-
~
12-
!!! error TS2369: A parameter property is only allowed in a constructor implementation.
139
~~~~~~~~
1410
!!! error TS2369: A parameter property is only allowed in a constructor implementation.
1511
static set X(public v2) { }
16-
~
17-
!!! error TS2369: A parameter property is only allowed in a constructor implementation.
1812
~~~~~~~~~
1913
!!! error TS2369: A parameter property is only allowed in a constructor implementation.
2014
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [declarationEmit_readonly.ts]
2+
3+
class C {
4+
constructor(readonly x: number) {}
5+
}
6+
7+
//// [declarationEmit_readonly.js]
8+
var C = (function () {
9+
function C(x) {
10+
this.x = x;
11+
}
12+
return C;
13+
}());
14+
15+
16+
//// [declarationEmit_readonly.d.ts]
17+
declare class C {
18+
readonly x: number;
19+
constructor(x: number);
20+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
=== tests/cases/conformance/classes/constructorDeclarations/constructorParameters/declarationEmit_readonly.ts ===
2+
3+
class C {
4+
>C : Symbol(C, Decl(declarationEmit_readonly.ts, 0, 0))
5+
6+
constructor(readonly x: number) {}
7+
>x : Symbol(C.x, Decl(declarationEmit_readonly.ts, 2, 16))
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
=== tests/cases/conformance/classes/constructorDeclarations/constructorParameters/declarationEmit_readonly.ts ===
2+
3+
class C {
4+
>C : C
5+
6+
constructor(readonly x: number) {}
7+
>x : number
8+
}
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
tests/cases/conformance/parser/ecmascript5/MemberAccessorDeclarations/parserMemberAccessorDeclaration15.ts(2,8): error TS2369: A parameter property is only allowed in a constructor implementation.
21
tests/cases/conformance/parser/ecmascript5/MemberAccessorDeclarations/parserMemberAccessorDeclaration15.ts(2,12): error TS2369: A parameter property is only allowed in a constructor implementation.
32

43

5-
==== tests/cases/conformance/parser/ecmascript5/MemberAccessorDeclarations/parserMemberAccessorDeclaration15.ts (2 errors) ====
4+
==== tests/cases/conformance/parser/ecmascript5/MemberAccessorDeclarations/parserMemberAccessorDeclaration15.ts (1 errors) ====
65
class C {
76
set Foo(public a: number) { }
8-
~~~
9-
!!! error TS2369: A parameter property is only allowed in a constructor implementation.
107
~~~~~~~~~~~~~~~~
118
!!! error TS2369: A parameter property is only allowed in a constructor implementation.
129
}

0 commit comments

Comments
 (0)