Skip to content
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
75 changes: 40 additions & 35 deletions tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4530,14 +4530,9 @@ func (c *Checker) areTypeParametersIdentical(declarations []*ast.Node, targetPar

func (c *Checker) checkBaseTypeAccessibility(t *Type, node *ast.Node) {
signatures := c.getSignaturesOfType(t, SignatureKindConstruct)
if len(signatures) != 0 {
declaration := signatures[0].declaration
if declaration != nil && ast.HasModifier(declaration, ast.ModifierFlagsPrivate) {
typeClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(t.symbol)
if !c.isNodeWithinClass(node, typeClassDeclaration) {
c.error(node, diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, c.getFullyQualifiedName(t.symbol, nil))
}
}
accessibilityError := c.getConstructorAccessibilityError(node, signatures, ast.ModifierFlagsPrivate)
if accessibilityError != nil {
c.error(node, diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, c.getFullyQualifiedName(accessibilityError.declaringClass.symbol, nil))
}
}

Expand Down Expand Up @@ -8777,7 +8772,14 @@ func (c *Checker) resolveNewExpression(node *ast.Node, candidatesOutArray *[]*Si
// that the user will not add any.
constructSignatures := c.getSignaturesOfType(expressionType, SignatureKindConstruct)
if len(constructSignatures) != 0 {
if !c.isConstructorAccessible(node, constructSignatures[0]) {
accessibilityError := c.getConstructorAccessibilityError(node, constructSignatures, ast.ModifierFlagsNonPublicAccessibilityModifier)
if accessibilityError != nil {
if accessibilityError.kind&ast.ModifierFlagsPrivate != 0 {
c.error(node, diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, c.TypeToString(accessibilityError.declaringClass))
}
if accessibilityError.kind&ast.ModifierFlagsProtected != 0 {
c.error(node, diagnostics.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration, c.TypeToString(accessibilityError.declaringClass))
}
return c.resolveErrorCall(node)
}
// If the expression is a class of abstract type, or an abstract construct signature,
Expand Down Expand Up @@ -8820,36 +8822,39 @@ func (c *Checker) resolveNewExpression(node *ast.Node, candidatesOutArray *[]*Si
return c.resolveErrorCall(node)
}

func (c *Checker) isConstructorAccessible(node *ast.Node, signature *Signature) bool {
if signature == nil || signature.declaration == nil {
return true
}
declaration := signature.declaration
modifiers := getSelectedModifierFlags(declaration, ast.ModifierFlagsNonPublicAccessibilityModifier)
// (1) Public constructors and (2) constructor functions are always accessible.
if modifiers == 0 || !ast.IsConstructorDeclaration(declaration) {
return true
}
declaringClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(declaration.Parent.Symbol())
declaringClass := c.getDeclaredTypeOfSymbol(declaration.Parent.Symbol())
// A private or protected constructor can only be instantiated within its own class (or a subclass, for protected)
if !c.isNodeWithinClass(node, declaringClassDeclaration) {
containingClass := ast.GetContainingClass(node)
if containingClass != nil && modifiers&ast.ModifierFlagsProtected != 0 {
containingType := c.getDeclaredTypeOfSymbol(containingClass.Symbol())
if c.typeHasProtectedAccessibleBase(declaration.Parent.Symbol(), containingType) {
return true
}
type constructorAccessibilityError struct {
kind ast.ModifierFlags
declaringClass *Type
}

func (c *Checker) getConstructorAccessibilityError(node *ast.Node, signatures []*Signature, modifiersMask ast.ModifierFlags) *constructorAccessibilityError {
for _, signature := range signatures {
if signature.declaration == nil {
continue
}
if modifiers&ast.ModifierFlagsPrivate != 0 {
c.error(node, diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, c.TypeToString(declaringClass))
declaration := signature.declaration
modifiers := getSelectedModifierFlags(declaration, modifiersMask)
// (1) Public constructors and (2) constructor functions are always accessible.
if modifiers == 0 || !ast.IsConstructorDeclaration(declaration) {
continue
}
if modifiers&ast.ModifierFlagsProtected != 0 {
c.error(node, diagnostics.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration, c.TypeToString(declaringClass))
declaringClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(declaration.Parent.Symbol())
// A private or protected constructor can only be instantiated within its own class (or a subclass, for protected)
if !c.isNodeWithinClass(node, declaringClassDeclaration) {
containingClass := ast.GetContainingClass(node)
if containingClass != nil && modifiers&ast.ModifierFlagsProtected != 0 {
containingType := c.getTypeOfNode(containingClass)
if c.typeHasProtectedAccessibleBase(declaration.Parent.Symbol(), containingType) {
continue
}
}
return &constructorAccessibilityError{
kind: modifiers,
declaringClass: c.getDeclaredTypeOfSymbol(declaration.Parent.Symbol()),
}
}
return false
}
return true
return nil
}

func (c *Checker) typeHasProtectedAccessibleBase(target *ast.Symbol, t *Type) bool {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
extendPrivateConstructorClass2.ts(10,1): error TS2673: Constructor of class 'A1' is private and only accessible within the class declaration.
extendPrivateConstructorClass2.ts(11,24): error TS2675: Cannot extend a class 'A1'. Class constructor is marked as private.
extendPrivateConstructorClass2.ts(22,1): error TS2673: Constructor of class 'B2' is private and only accessible within the class declaration.
extendPrivateConstructorClass2.ts(23,24): error TS2675: Cannot extend a class 'B2'. Class constructor is marked as private.
extendPrivateConstructorClass2.ts(33,26): error TS2675: Cannot extend a class 'j1'. Class constructor is marked as private.
extendPrivateConstructorClass2.ts(79,26): error TS2510: Base constructors must all have the same return type.
extendPrivateConstructorClass2.ts(98,22): error TS2510: Base constructors must all have the same return type.
extendPrivateConstructorClass2.ts(98,22): error TS2675: Cannot extend a class 'j10'. Class constructor is marked as private.
extendPrivateConstructorClass2.ts(129,22): error TS2675: Cannot extend a class 'j14'. Class constructor is marked as private.


==== extendPrivateConstructorClass2.ts (9 errors) ====
class A1 {
private constructor(arg: string) {}
}
class B1 {
constructor(arg: number) {}
}

declare const Cls1: typeof A1 & typeof B1;

new Cls1(42); // error
~~~~~~~~~~~~
!!! error TS2673: Constructor of class 'A1' is private and only accessible within the class declaration.
class Derived1 extends Cls1 {} // error
~~~~
!!! error TS2675: Cannot extend a class 'A1'. Class constructor is marked as private.

class A2 {
constructor(arg: string) {}
}
class B2 {
private constructor(arg: number) {}
}

declare const Cls2: typeof A2 & typeof B2;

new Cls2(42); // error
~~~~~~~~~~~~
!!! error TS2673: Constructor of class 'B2' is private and only accessible within the class declaration.
class Derived2 extends Cls2 {} // error
~~~~
!!! error TS2675: Cannot extend a class 'B2'. Class constructor is marked as private.

// https://github.com/microsoft/TypeScript/issues/62614
declare abstract class j1 {
private constructor(...args: any[]);
}
declare abstract class j2 {
private constructor(...args: any[]);
}
declare const jS: typeof j1 & typeof j2;
declare class j0 extends jS {} // error
~~
!!! error TS2675: Cannot extend a class 'j1'. Class constructor is marked as private.

abstract class j3 {
private constructor(...args: any[]) {}
method1() {
abstract class j4 {
private constructor(...args: any[]) {}
method2() {
const jS: typeof j3 & typeof j4 = null!;

// bizarre but ok
class j0 extends jS {
method1() {}
method2() {}
}
}
}
}
}

abstract class j5 {
private constructor(...args: any[]) {}
method1() {
abstract class j6 {
private constructor(...args: any[]) {}
method2() {}
}
const jS: typeof j5 & typeof j6 = null!;

// bizarre but ok too given the base is a result of a mixin
class j0 extends jS {
method1() {}
method2() {}
}
}
}

abstract class j7 {
private constructor(arg: string) {}
method1() {
abstract class j8 {
private constructor(arg: number) {}
method2() {
const jS: typeof j7 & typeof j8 = null!;

// error
class j0 extends jS {
~~
!!! error TS2510: Base constructors must all have the same return type.
method1() {}
method2() {}
}
}
}
}
}

abstract class j9 {
private constructor(arg: string) {}
method1() {
abstract class j10 {
private constructor(arg: number) {}
method2() {}
}
const jS: typeof j9 & typeof j10 = null!;

// error
class j0 extends jS {
~~
!!! error TS2510: Base constructors must all have the same return type.
~~
!!! error TS2675: Cannot extend a class 'j10'. Class constructor is marked as private.
method1() {}
method2() {}
}
}
}

abstract class j11 {
private constructor(arg: string) {}
static {
abstract class j12 {
private constructor(arg: number) {}
static {
const jS: typeof j11 & typeof j12 = null!;

// ok
class j0 extends jS {}
}
}
}
}

abstract class j13 {
private constructor(arg: string) {}
static {
abstract class j14 {
private constructor(arg: number) {}
}
const jS: typeof j13 & typeof j14 = null!;

// error
class j0 extends jS {}
~~
!!! error TS2675: Cannot extend a class 'j14'. Class constructor is marked as private.
}
}

Loading