Fix #7590: Allow 'readonly' to be used in constructor parameters

This commit is contained in:
Andy Hanson 2016-05-11 08:29:03 -07:00
parent b68e93966a
commit ad2634e343
14 changed files with 133 additions and 8 deletions

View file

@ -12658,7 +12658,7 @@ namespace ts {
checkVariableLikeDeclaration(node);
let func = getContainingFunction(node);
if (node.flags & NodeFlags.AccessibilityModifier) {
if (node.flags & NodeFlags.ConstructorParameterModifier) {
func = getContainingFunction(node);
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
@ -12994,7 +12994,7 @@ namespace ts {
// or the containing class declares instance member variables with initializers.
const superCallShouldBeFirst =
forEach((<ClassDeclaration>node.parent).members, isInstancePropertyWithInitializer) ||
forEach(node.parameters, p => p.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected));
forEach(node.parameters, p => p.flags & NodeFlags.ConstructorParameterModifier);
// Skip past any prologue directives to find the first statement
// to ensure that it was a super call.
@ -17651,7 +17651,8 @@ namespace ts {
if (flags & NodeFlags.Readonly) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly");
}
else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature) {
else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature &&
!(node.kind == SyntaxKind.Parameter && isParameterPropertyDeclaration(<ParameterDeclaration> node))) {
return grammarErrorOnNode(modifier, Diagnostics.readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature);
}
flags |= NodeFlags.Readonly;
@ -17759,7 +17760,7 @@ namespace ts {
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & NodeFlags.Ambient) {
return grammarErrorOnNode(lastDeclare, Diagnostics.A_0_modifier_cannot_be_used_with_an_import_declaration, "declare");
}
else if (node.kind === SyntaxKind.Parameter && (flags & NodeFlags.AccessibilityModifier) && isBindingPattern((<ParameterDeclaration>node).name)) {
else if (node.kind === SyntaxKind.Parameter && (flags & NodeFlags.ConstructorParameterModifier) && isBindingPattern((<ParameterDeclaration>node).name)) {
return grammarErrorOnNode(node, Diagnostics.A_parameter_property_may_not_be_a_binding_pattern);
}
if (flags & NodeFlags.Async) {

View file

@ -1051,7 +1051,7 @@ namespace ts {
function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) {
if (constructorDeclaration) {
forEach(constructorDeclaration.parameters, param => {
if (param.flags & NodeFlags.AccessibilityModifier) {
if (param.flags & NodeFlags.ConstructorParameterModifier) {
emitPropertyDeclaration(param);
}
});

View file

@ -4979,7 +4979,7 @@ const _super = (function (geti, seti) {
function emitParameterPropertyAssignments(node: ConstructorDeclaration) {
forEach(node.parameters, param => {
if (param.flags & NodeFlags.AccessibilityModifier) {
if (param.flags & NodeFlags.ConstructorParameterModifier) {
writeLine();
emitStart(param);
emitStart(param.name);

View file

@ -407,8 +407,10 @@ namespace ts {
HasAggregatedChildData = 1 << 29, // If we've computed data from children and cached it in this node
HasJsxSpreadAttribute = 1 << 30,
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async,
Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async | Readonly,
AccessibilityModifier = Public | Private | Protected,
// Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property.
ConstructorParameterModifier = AccessibilityModifier | Readonly,
BlockScoped = Let | Const,
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,

View file

@ -3021,7 +3021,7 @@ namespace ts {
}
export function isParameterPropertyDeclaration(node: ParameterDeclaration): boolean {
return node.flags & NodeFlags.AccessibilityModifier && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
return node.flags & NodeFlags.ConstructorParameterModifier && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
}
export function startsWith(str: string, prefix: string): boolean {

View file

@ -0,0 +1,9 @@
tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyInAmbientClass.ts(2,14): error TS2369: A parameter property is only allowed in a constructor implementation.
==== tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyInAmbientClass.ts (1 errors) ====
declare class C{
constructor(readonly x: number);
~~~~~~~~~~~~~~~~~~
!!! error TS2369: A parameter property is only allowed in a constructor implementation.
}

View file

@ -0,0 +1,6 @@
//// [readonlyInAmbientClass.ts]
declare class C{
constructor(readonly x: number);
}
//// [readonlyInAmbientClass.js]

View file

@ -0,0 +1,25 @@
tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyInConstructorParameters.ts(4,1): error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyInConstructorParameters.ts(7,26): error TS1029: 'public' modifier must precede 'readonly' modifier.
tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyInConstructorParameters.ts(13,10): error TS2341: Property 'x' is private and only accessible within class 'F'.
==== tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyInConstructorParameters.ts (3 errors) ====
class C {
constructor(readonly x: number) {}
}
new C(1).x = 2;
~~~~~~~~~~
!!! error TS2450: Left-hand side of assignment expression cannot be a constant or a read-only property.
class E {
constructor(readonly public x: number) {}
~~~~~~
!!! error TS1029: 'public' modifier must precede 'readonly' modifier.
}
class F {
constructor(private readonly x: number) {}
}
new F(1).x;
~
!!! error TS2341: Property 'x' is private and only accessible within class 'F'.

View file

@ -0,0 +1,36 @@
//// [readonlyInConstructorParameters.ts]
class C {
constructor(readonly x: number) {}
}
new C(1).x = 2;
class E {
constructor(readonly public x: number) {}
}
class F {
constructor(private readonly x: number) {}
}
new F(1).x;
//// [readonlyInConstructorParameters.js]
var C = (function () {
function C(x) {
this.x = x;
}
return C;
}());
new C(1).x = 2;
var E = (function () {
function E(x) {
this.x = x;
}
return E;
}());
var F = (function () {
function F(x) {
this.x = x;
}
return F;
}());
new F(1).x;

View file

@ -0,0 +1,13 @@
tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyReadonly.ts(2,14): error TS1030: 'readonly' modifier already seen.
tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyReadonly.ts(3,26): error TS1030: 'readonly' modifier already seen.
==== tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyReadonly.ts (2 errors) ====
class C {
readonly readonly x: number;
~~~~~~~~
!!! error TS1030: 'readonly' modifier already seen.
constructor(readonly readonly y: number) {}
~~~~~~~~
!!! error TS1030: 'readonly' modifier already seen.
}

View file

@ -0,0 +1,13 @@
//// [readonlyReadonly.ts]
class C {
readonly readonly x: number;
constructor(readonly readonly y: number) {}
}
//// [readonlyReadonly.js]
var C = (function () {
function C(y) {
this.y = y;
}
return C;
}());

View file

@ -0,0 +1,3 @@
declare class C{
constructor(readonly x: number);
}

View file

@ -0,0 +1,13 @@
class C {
constructor(readonly x: number) {}
}
new C(1).x = 2;
class E {
constructor(readonly public x: number) {}
}
class F {
constructor(private readonly x: number) {}
}
new F(1).x;

View file

@ -0,0 +1,4 @@
class C {
readonly readonly x: number;
constructor(readonly readonly y: number) {}
}