diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f4ee7960f1074..7eae4e8bb062a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -40609,6 +40609,7 @@ namespace ts { } break; case SyntaxKind.OverrideKeyword: + // If node.kind === SyntaxKind.Parameter, checkParameter reports an error if it's not a parameter property. if (flags & ModifierFlags.Override) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "override"); } @@ -40621,9 +40622,6 @@ namespace ts { else if (flags & ModifierFlags.Async) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "override", "async"); } - if (node.kind === SyntaxKind.Parameter) { - return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "override"); - } flags |= ModifierFlags.Override; lastOverride = modifier; break; @@ -40696,7 +40694,7 @@ namespace ts { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "readonly"); } else if (node.kind !== SyntaxKind.PropertyDeclaration && node.kind !== SyntaxKind.PropertySignature && node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.Parameter) { - // If node.kind === SyntaxKind.Parameter, checkParameter report an error if it's not a parameter property. + // If node.kind === SyntaxKind.Parameter, checkParameter reports an error if it's not a parameter property. return grammarErrorOnNode(modifier, Diagnostics.readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature); } flags |= ModifierFlags.Readonly; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 780050afed5b5..e488333bbad00 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -806,7 +806,7 @@ namespace ts { AccessibilityModifier = Public | Private | Protected, // Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property. - ParameterPropertyModifier = AccessibilityModifier | Readonly, + ParameterPropertyModifier = AccessibilityModifier | Readonly | Override, NonPublicAccessibilityModifier = Private | Protected, TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const | Override, diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 55462526ae6f6..736581022dcbf 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -1175,7 +1175,7 @@ namespace FourSlashInterface { export const classElementInJsKeywords = getInJsKeywords(classElementKeywords); export const constructorParameterKeywords: readonly ExpectedCompletionEntryObject[] = - ["private", "protected", "public", "readonly"].map((name): ExpectedCompletionEntryObject => ({ + ["private", "protected", "public", "readonly", "override"].map((name): ExpectedCompletionEntryObject => ({ name, kind: "keyword", sortText: SortText.GlobalsOrKeywords diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ea64a21ef4294..e94b1da823f34 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -540,7 +540,7 @@ declare namespace ts { Override = 16384, HasComputedFlags = 536870912, AccessibilityModifier = 28, - ParameterPropertyModifier = 92, + ParameterPropertyModifier = 16476, NonPublicAccessibilityModifier = 24, TypeScriptModifier = 18654, ExportDefault = 513, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 859a76db3394b..7b8a24ba088b4 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -540,7 +540,7 @@ declare namespace ts { Override = 16384, HasComputedFlags = 536870912, AccessibilityModifier = 28, - ParameterPropertyModifier = 92, + ParameterPropertyModifier = 16476, NonPublicAccessibilityModifier = 24, TypeScriptModifier = 18654, ExportDefault = 513, diff --git a/tests/baselines/reference/override11.errors.txt b/tests/baselines/reference/override11.errors.txt index 0af9f32dd0a65..2a0d38fa187b5 100644 --- a/tests/baselines/reference/override11.errors.txt +++ b/tests/baselines/reference/override11.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/override/override11.ts(6,18): error TS1090: 'override' modifier cannot appear on a parameter. +tests/cases/conformance/override/override11.ts(6,27): error TS1029: 'public' modifier must precede 'override' modifier. ==== tests/cases/conformance/override/override11.ts (1 errors) ==== @@ -8,8 +8,8 @@ tests/cases/conformance/override/override11.ts(6,18): error TS1090: 'override' m class Sub extends Base { constructor (override public foo: number) { - ~~~~~~~~ -!!! error TS1090: 'override' modifier cannot appear on a parameter. + ~~~~~~ +!!! error TS1029: 'public' modifier must precede 'override' modifier. super(); } } diff --git a/tests/baselines/reference/overrideParameterProperty.errors.txt b/tests/baselines/reference/overrideParameterProperty.errors.txt new file mode 100644 index 0000000000000..10929ce6e3cae --- /dev/null +++ b/tests/baselines/reference/overrideParameterProperty.errors.txt @@ -0,0 +1,36 @@ +tests/cases/conformance/override/overrideParameterProperty.ts(20,24): error TS1029: 'public' modifier must precede 'override' modifier. +tests/cases/conformance/override/overrideParameterProperty.ts(25,5): error TS2369: A parameter property is only allowed in a constructor implementation. + + +==== tests/cases/conformance/override/overrideParameterProperty.ts (2 errors) ==== + class Base { + p1!: string; + } + + class C1 extends Base { + constructor(public override p1: "hello") { + super(); + this.p1; + } + } + + class C2 extends Base { + constructor(override p1: "hello") { + super(); + this.p1; + } + } + + class C3 extends Base { + constructor(override public p1: "hello") { + ~~~~~~ +!!! error TS1029: 'public' modifier must precede 'override' modifier. + super(); + this.p1; + } + + m(override p1: "hello") {} + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2369: A parameter property is only allowed in a constructor implementation. + } + \ No newline at end of file diff --git a/tests/baselines/reference/overrideParameterProperty.js b/tests/baselines/reference/overrideParameterProperty.js new file mode 100644 index 0000000000000..ad063d8e5ef8f --- /dev/null +++ b/tests/baselines/reference/overrideParameterProperty.js @@ -0,0 +1,81 @@ +//// [overrideParameterProperty.ts] +class Base { + p1!: string; +} + +class C1 extends Base { + constructor(public override p1: "hello") { + super(); + this.p1; + } +} + +class C2 extends Base { + constructor(override p1: "hello") { + super(); + this.p1; + } +} + +class C3 extends Base { + constructor(override public p1: "hello") { + super(); + this.p1; + } + + m(override p1: "hello") {} +} + + +//// [overrideParameterProperty.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var Base = /** @class */ (function () { + function Base() { + } + return Base; +}()); +var C1 = /** @class */ (function (_super) { + __extends(C1, _super); + function C1(p1) { + var _this = _super.call(this) || this; + _this.p1 = p1; + _this.p1; + return _this; + } + return C1; +}(Base)); +var C2 = /** @class */ (function (_super) { + __extends(C2, _super); + function C2(p1) { + var _this = _super.call(this) || this; + _this.p1 = p1; + _this.p1; + return _this; + } + return C2; +}(Base)); +var C3 = /** @class */ (function (_super) { + __extends(C3, _super); + function C3(p1) { + var _this = _super.call(this) || this; + _this.p1 = p1; + _this.p1; + return _this; + } + C3.prototype.m = function (p1) { }; + return C3; +}(Base)); diff --git a/tests/baselines/reference/overrideParameterProperty.symbols b/tests/baselines/reference/overrideParameterProperty.symbols new file mode 100644 index 0000000000000..88eefef58290e --- /dev/null +++ b/tests/baselines/reference/overrideParameterProperty.symbols @@ -0,0 +1,63 @@ +=== tests/cases/conformance/override/overrideParameterProperty.ts === +class Base { +>Base : Symbol(Base, Decl(overrideParameterProperty.ts, 0, 0)) + + p1!: string; +>p1 : Symbol(Base.p1, Decl(overrideParameterProperty.ts, 0, 12)) +} + +class C1 extends Base { +>C1 : Symbol(C1, Decl(overrideParameterProperty.ts, 2, 1)) +>Base : Symbol(Base, Decl(overrideParameterProperty.ts, 0, 0)) + + constructor(public override p1: "hello") { +>p1 : Symbol(C1.p1, Decl(overrideParameterProperty.ts, 5, 14)) + + super(); +>super : Symbol(Base, Decl(overrideParameterProperty.ts, 0, 0)) + + this.p1; +>this.p1 : Symbol(C1.p1, Decl(overrideParameterProperty.ts, 5, 14)) +>this : Symbol(C1, Decl(overrideParameterProperty.ts, 2, 1)) +>p1 : Symbol(C1.p1, Decl(overrideParameterProperty.ts, 5, 14)) + } +} + +class C2 extends Base { +>C2 : Symbol(C2, Decl(overrideParameterProperty.ts, 9, 1)) +>Base : Symbol(Base, Decl(overrideParameterProperty.ts, 0, 0)) + + constructor(override p1: "hello") { +>p1 : Symbol(C2.p1, Decl(overrideParameterProperty.ts, 12, 14)) + + super(); +>super : Symbol(Base, Decl(overrideParameterProperty.ts, 0, 0)) + + this.p1; +>this.p1 : Symbol(C2.p1, Decl(overrideParameterProperty.ts, 12, 14)) +>this : Symbol(C2, Decl(overrideParameterProperty.ts, 9, 1)) +>p1 : Symbol(C2.p1, Decl(overrideParameterProperty.ts, 12, 14)) + } +} + +class C3 extends Base { +>C3 : Symbol(C3, Decl(overrideParameterProperty.ts, 16, 1)) +>Base : Symbol(Base, Decl(overrideParameterProperty.ts, 0, 0)) + + constructor(override public p1: "hello") { +>p1 : Symbol(C3.p1, Decl(overrideParameterProperty.ts, 19, 14)) + + super(); +>super : Symbol(Base, Decl(overrideParameterProperty.ts, 0, 0)) + + this.p1; +>this.p1 : Symbol(C3.p1, Decl(overrideParameterProperty.ts, 19, 14)) +>this : Symbol(C3, Decl(overrideParameterProperty.ts, 16, 1)) +>p1 : Symbol(C3.p1, Decl(overrideParameterProperty.ts, 19, 14)) + } + + m(override p1: "hello") {} +>m : Symbol(C3.m, Decl(overrideParameterProperty.ts, 22, 3)) +>p1 : Symbol(p1, Decl(overrideParameterProperty.ts, 24, 4)) +} + diff --git a/tests/baselines/reference/overrideParameterProperty.types b/tests/baselines/reference/overrideParameterProperty.types new file mode 100644 index 0000000000000..33aca1cfbca47 --- /dev/null +++ b/tests/baselines/reference/overrideParameterProperty.types @@ -0,0 +1,66 @@ +=== tests/cases/conformance/override/overrideParameterProperty.ts === +class Base { +>Base : Base + + p1!: string; +>p1 : string +} + +class C1 extends Base { +>C1 : C1 +>Base : Base + + constructor(public override p1: "hello") { +>p1 : "hello" + + super(); +>super() : void +>super : typeof Base + + this.p1; +>this.p1 : "hello" +>this : this +>p1 : "hello" + } +} + +class C2 extends Base { +>C2 : C2 +>Base : Base + + constructor(override p1: "hello") { +>p1 : "hello" + + super(); +>super() : void +>super : typeof Base + + this.p1; +>this.p1 : "hello" +>this : this +>p1 : "hello" + } +} + +class C3 extends Base { +>C3 : C3 +>Base : Base + + constructor(override public p1: "hello") { +>p1 : "hello" + + super(); +>super() : void +>super : typeof Base + + this.p1; +>this.p1 : "hello" +>this : this +>p1 : "hello" + } + + m(override p1: "hello") {} +>m : (p1: "hello") => void +>p1 : "hello" +} + diff --git a/tests/cases/conformance/override/overrideParameterProperty.ts b/tests/cases/conformance/override/overrideParameterProperty.ts new file mode 100644 index 0000000000000..67e25674010a2 --- /dev/null +++ b/tests/cases/conformance/override/overrideParameterProperty.ts @@ -0,0 +1,28 @@ +// @noImplicitOverride: true + +class Base { + p1!: string; +} + +class C1 extends Base { + constructor(public override p1: "hello") { + super(); + this.p1; + } +} + +class C2 extends Base { + constructor(override p1: "hello") { + super(); + this.p1; + } +} + +class C3 extends Base { + constructor(override public p1: "hello") { + super(); + this.p1; + } + + m(override p1: "hello") {} +}