Skip to content

Protected constructors accessible in subclass static methods #9904

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11544,8 +11544,20 @@ namespace ts {
const declaringClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(declaration.parent.symbol);
const declaringClass = <InterfaceType>getDeclaredTypeOfSymbol(declaration.parent.symbol);

// A private or protected constructor can only be instantiated within it's own class
// A private or protected constructor can only be instantiated within its own class (or a subclass, for protected)
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
const containingClass = getContainingClass(node);
if (containingClass) {
const containingType = getTypeOfNode(containingClass);
const baseTypes = getBaseTypes(<InterfaceType>containingType);
if (baseTypes.length) {
const baseType = baseTypes[0];
if (flags & NodeFlags.Protected &&
baseType.symbol === declaration.parent.symbol) {
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it matter whether the function is static?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. I wasn't paying attention and overfit to the example. I see that instance methods should be ok based on your constructor-with-super equivalence example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
}
}
if (flags & NodeFlags.Private) {
error(node, Diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, typeToString(declaringClass));
}
Expand Down
41 changes: 23 additions & 18 deletions tests/baselines/reference/classConstructorAccessibility2.errors.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(26,28): error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(29,24): error TS2675: Cannot extend a class 'BaseC'. Class constructor is marked as private.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(32,28): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(36,10): error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(37,10): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(32,24): error TS2675: Cannot extend a class 'BaseC'. Class constructor is marked as private.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(35,28): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(36,35): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(40,10): error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration.
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(41,10): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration.


==== tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts (5 errors) ====
Expand All @@ -14,35 +14,39 @@ tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessib

class BaseB {
protected constructor(public x: number) { }
createInstance() { new BaseB(1); }
createInstance() { new BaseB(2); }
}

class BaseC {
private constructor(public x: number) { }
createInstance() { new BaseC(1); }
private constructor(public x: number) { }
createInstance() { new BaseC(3); }
static staticInstance() { new BaseC(4); }
}

class DerivedA extends BaseA {
constructor(public x: number) { super(x); }
createInstance() { new DerivedA(1); }
createBaseInstance() { new BaseA(1); }
createInstance() { new DerivedA(5); }
createBaseInstance() { new BaseA(6); }
static staticBaseInstance() { new BaseA(7); }
}

class DerivedB extends BaseB {
constructor(public x: number) { super(x); }
createInstance() { new DerivedB(1); }
createBaseInstance() { new BaseB(1); } // error
~~~~~~~~~~~~
!!! error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration.
createInstance() { new DerivedB(7); }
createBaseInstance() { new BaseB(8); } // ok
static staticBaseInstance() { new BaseB(9); } // ok
}

class DerivedC extends BaseC { // error
~~~~~
!!! error TS2675: Cannot extend a class 'BaseC'. Class constructor is marked as private.
constructor(public x: number) { super(x); }
createInstance() { new DerivedC(1); }
createBaseInstance() { new BaseC(1); } // error
~~~~~~~~~~~~
createInstance() { new DerivedC(9); }
createBaseInstance() { new BaseC(10); } // error
~~~~~~~~~~~~~
!!! error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration.
static staticBaseInstance() { new BaseC(11); } // error
~~~~~~~~~~~~~
!!! error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration.
}

Expand All @@ -56,4 +60,5 @@ tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessib

var da = new DerivedA(1);
var db = new DerivedB(1);
var dc = new DerivedC(1);
var dc = new DerivedC(1);

49 changes: 31 additions & 18 deletions tests/baselines/reference/classConstructorAccessibility2.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,34 @@ class BaseA {

class BaseB {
protected constructor(public x: number) { }
createInstance() { new BaseB(1); }
createInstance() { new BaseB(2); }
}

class BaseC {
private constructor(public x: number) { }
createInstance() { new BaseC(1); }
private constructor(public x: number) { }
createInstance() { new BaseC(3); }
static staticInstance() { new BaseC(4); }
}

class DerivedA extends BaseA {
constructor(public x: number) { super(x); }
createInstance() { new DerivedA(1); }
createBaseInstance() { new BaseA(1); }
createInstance() { new DerivedA(5); }
createBaseInstance() { new BaseA(6); }
static staticBaseInstance() { new BaseA(7); }
}

class DerivedB extends BaseB {
constructor(public x: number) { super(x); }
createInstance() { new DerivedB(1); }
createBaseInstance() { new BaseB(1); } // error
createInstance() { new DerivedB(7); }
createBaseInstance() { new BaseB(8); } // ok
static staticBaseInstance() { new BaseB(9); } // ok
}

class DerivedC extends BaseC { // error
constructor(public x: number) { super(x); }
createInstance() { new DerivedC(1); }
createBaseInstance() { new BaseC(1); } // error
createInstance() { new DerivedC(9); }
createBaseInstance() { new BaseC(10); } // error
static staticBaseInstance() { new BaseC(11); } // error
}

var ba = new BaseA(1);
Expand All @@ -39,7 +43,8 @@ var bc = new BaseC(1); // error

var da = new DerivedA(1);
var db = new DerivedB(1);
var dc = new DerivedC(1);
var dc = new DerivedC(1);


//// [classConstructorAccessibility2.js]
var __extends = (this && this.__extends) || function (d, b) {
Expand All @@ -58,14 +63,15 @@ var BaseB = (function () {
function BaseB(x) {
this.x = x;
}
BaseB.prototype.createInstance = function () { new BaseB(1); };
BaseB.prototype.createInstance = function () { new BaseB(2); };
return BaseB;
}());
var BaseC = (function () {
function BaseC(x) {
this.x = x;
}
BaseC.prototype.createInstance = function () { new BaseC(1); };
BaseC.prototype.createInstance = function () { new BaseC(3); };
BaseC.staticInstance = function () { new BaseC(4); };
return BaseC;
}());
var DerivedA = (function (_super) {
Expand All @@ -74,8 +80,9 @@ var DerivedA = (function (_super) {
_super.call(this, x);
this.x = x;
}
DerivedA.prototype.createInstance = function () { new DerivedA(1); };
DerivedA.prototype.createBaseInstance = function () { new BaseA(1); };
DerivedA.prototype.createInstance = function () { new DerivedA(5); };
DerivedA.prototype.createBaseInstance = function () { new BaseA(6); };
DerivedA.staticBaseInstance = function () { new BaseA(7); };
return DerivedA;
}(BaseA));
var DerivedB = (function (_super) {
Expand All @@ -84,8 +91,9 @@ var DerivedB = (function (_super) {
_super.call(this, x);
this.x = x;
}
DerivedB.prototype.createInstance = function () { new DerivedB(1); };
DerivedB.prototype.createBaseInstance = function () { new BaseB(1); }; // error
DerivedB.prototype.createInstance = function () { new DerivedB(7); };
DerivedB.prototype.createBaseInstance = function () { new BaseB(8); }; // ok
DerivedB.staticBaseInstance = function () { new BaseB(9); }; // ok
return DerivedB;
}(BaseB));
var DerivedC = (function (_super) {
Expand All @@ -94,8 +102,9 @@ var DerivedC = (function (_super) {
_super.call(this, x);
this.x = x;
}
DerivedC.prototype.createInstance = function () { new DerivedC(1); };
DerivedC.prototype.createBaseInstance = function () { new BaseC(1); }; // error
DerivedC.prototype.createInstance = function () { new DerivedC(9); };
DerivedC.prototype.createBaseInstance = function () { new BaseC(10); }; // error
DerivedC.staticBaseInstance = function () { new BaseC(11); }; // error
return DerivedC;
}(BaseC));
var ba = new BaseA(1);
Expand All @@ -121,24 +130,28 @@ declare class BaseC {
x: number;
private constructor(x);
createInstance(): void;
static staticInstance(): void;
}
declare class DerivedA extends BaseA {
x: number;
constructor(x: number);
createInstance(): void;
createBaseInstance(): void;
static staticBaseInstance(): void;
}
declare class DerivedB extends BaseB {
x: number;
constructor(x: number);
createInstance(): void;
createBaseInstance(): void;
static staticBaseInstance(): void;
}
declare class DerivedC extends BaseC {
x: number;
constructor(x: number);
createInstance(): void;
createBaseInstance(): void;
static staticBaseInstance(): void;
}
declare var ba: BaseA;
declare var bb: any;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility5.ts(9,21): error TS2674: Constructor of class 'Base' is protected and only accessible within the class declaration.


==== tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility5.ts (1 errors) ====
class Base {
protected constructor() { }
}
class Derived extends Base {
static make() { new Base() } // ok
}

class Unrelated {
static fake() { new Base() } // error
~~~~~~~~~~
!!! error TS2674: Constructor of class 'Base' is protected and only accessible within the class declaration.
}

38 changes: 38 additions & 0 deletions tests/baselines/reference/classConstructorAccessibility5.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//// [classConstructorAccessibility5.ts]
class Base {
protected constructor() { }
}
class Derived extends Base {
static make() { new Base() } // ok
}

class Unrelated {
static fake() { new Base() } // error
}


//// [classConstructorAccessibility5.js]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Base = (function () {
function Base() {
}
return Base;
}());
var Derived = (function (_super) {
__extends(Derived, _super);
function Derived() {
_super.apply(this, arguments);
}
Derived.make = function () { new Base(); }; // ok
return Derived;
}(Base));
var Unrelated = (function () {
function Unrelated() {
}
Unrelated.fake = function () { new Base(); }; // error
return Unrelated;
}());
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,34 @@ class BaseA {

class BaseB {
protected constructor(public x: number) { }
createInstance() { new BaseB(1); }
createInstance() { new BaseB(2); }
}

class BaseC {
private constructor(public x: number) { }
createInstance() { new BaseC(1); }
private constructor(public x: number) { }
createInstance() { new BaseC(3); }
static staticInstance() { new BaseC(4); }
}

class DerivedA extends BaseA {
constructor(public x: number) { super(x); }
createInstance() { new DerivedA(1); }
createBaseInstance() { new BaseA(1); }
createInstance() { new DerivedA(5); }
createBaseInstance() { new BaseA(6); }
static staticBaseInstance() { new BaseA(7); }
}

class DerivedB extends BaseB {
constructor(public x: number) { super(x); }
createInstance() { new DerivedB(1); }
createBaseInstance() { new BaseB(1); } // error
createInstance() { new DerivedB(7); }
createBaseInstance() { new BaseB(8); } // ok
static staticBaseInstance() { new BaseB(9); } // ok
}

class DerivedC extends BaseC { // error
constructor(public x: number) { super(x); }
createInstance() { new DerivedC(1); }
createBaseInstance() { new BaseC(1); } // error
createInstance() { new DerivedC(9); }
createBaseInstance() { new BaseC(10); } // error
static staticBaseInstance() { new BaseC(11); } // error
}

var ba = new BaseA(1);
Expand All @@ -39,4 +43,4 @@ var bc = new BaseC(1); // error

var da = new DerivedA(1);
var db = new DerivedB(1);
var dc = new DerivedC(1);
var dc = new DerivedC(1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class Base {
protected constructor() { }
}
class Derived extends Base {
static make() { new Base() } // ok
}

class Unrelated {
static fake() { new Base() } // error
}