Skip to content

Commit f869b41

Browse files
committed
Added private and protected modifiers to constructors
1 parent 6cc44d1 commit f869b41

2 files changed

Lines changed: 101 additions & 16 deletions

File tree

src/compiler/checker.ts

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5889,16 +5889,20 @@ namespace ts {
58895889

58905890
const sourceSignatures = getSignaturesOfType(source, kind);
58915891
const targetSignatures = getSignaturesOfType(target, kind);
5892-
if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length &&
5893-
isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
5894-
// An abstract constructor type is not assignable to a non-abstract constructor type
5895-
// as it would otherwise be possible to new an abstract class. Note that the assignablity
5896-
// check we perform for an extends clause excludes construct signatures from the target,
5897-
// so this check never proceeds.
5898-
if (reportErrors) {
5899-
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
5892+
if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length) {
5893+
if (isAbstractConstructorType(source) && !isAbstractConstructorType(target)) {
5894+
// An abstract constructor type is not assignable to a non-abstract constructor type
5895+
// as it would otherwise be possible to new an abstract class. Note that the assignablity
5896+
// check we perform for an extends clause excludes construct signatures from the target,
5897+
// so this check never proceeds.
5898+
if (reportErrors) {
5899+
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
5900+
}
5901+
return Ternary.False;
5902+
}
5903+
if (!constructorRelatedTo(sourceSignatures[0], targetSignatures[0], reportErrors)) {
5904+
return Ternary.False;
59005905
}
5901-
return Ternary.False;
59025906
}
59035907

59045908
let result = Ternary.True;
@@ -6052,6 +6056,32 @@ namespace ts {
60526056
}
60536057
return Ternary.True;
60546058
}
6059+
6060+
function constructorRelatedTo(sourceSignature: Signature, targetSignature: Signature, reportErrors: boolean) {
6061+
if (sourceSignature && targetSignature && sourceSignature.declaration && targetSignature.declaration) {
6062+
const sourceAccessibility = sourceSignature.declaration.flags & (NodeFlags.Private | NodeFlags.Protected);
6063+
const targetAccessibility = targetSignature.declaration.flags & (NodeFlags.Private | NodeFlags.Protected);
6064+
6065+
const isRelated = sourceAccessibility === targetAccessibility;
6066+
if (!isRelated && reportErrors) {
6067+
reportError(Diagnostics.Cannot_assign_a_0_constructor_type_to_a_1_constructor_type, flagsToString(sourceAccessibility), flagsToString(targetAccessibility));
6068+
}
6069+
6070+
return isRelated;
6071+
}
6072+
6073+
return true;
6074+
6075+
function flagsToString(flags: NodeFlags) {
6076+
if (flags === NodeFlags.Private) {
6077+
return "private";
6078+
}
6079+
if (flags === NodeFlags.Protected) {
6080+
return "protected";
6081+
}
6082+
return "public";
6083+
}
6084+
}
60556085
}
60566086

60576087
// Return true if the given type is the constructor type for an abstract class
@@ -10103,6 +10133,9 @@ namespace ts {
1010310133
// that the user will not add any.
1010410134
const constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct);
1010510135
if (constructSignatures.length) {
10136+
if (!isConstructorAccessible(node, constructSignatures[0])) {
10137+
return resolveErrorCall(node);
10138+
}
1010610139
return resolveCall(node, constructSignatures, candidatesOutArray);
1010710140
}
1010810141

@@ -10123,6 +10156,37 @@ namespace ts {
1012310156
return resolveErrorCall(node);
1012410157
}
1012510158

10159+
function isConstructorAccessible(node: NewExpression, signature: Signature) {
10160+
if (!signature || !signature.declaration) {
10161+
return true;
10162+
}
10163+
10164+
const declaration = signature.declaration;
10165+
const flags = declaration.flags;
10166+
10167+
// Public constructor is accessible.
10168+
if (!(flags & (NodeFlags.Private | NodeFlags.Protected))) {
10169+
return true;
10170+
}
10171+
10172+
const declaringClass = <InterfaceType>getDeclaredTypeOfSymbol(declaration.parent.symbol);
10173+
const enclosingClassDeclaration = getContainingClass(node);
10174+
const enclosingClass = enclosingClassDeclaration ? <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClassDeclaration)) : undefined;
10175+
10176+
// A private or protected constructor can only be instantiated within it's own class
10177+
if (declaringClass !== enclosingClass) {
10178+
if (flags & NodeFlags.Private) {
10179+
error(node, Diagnostics.Constructor_of_type_0_is_private_and_only_accessible_within_class_1, signatureToString(signature), typeToString(declaringClass));
10180+
}
10181+
if (flags & NodeFlags.Protected) {
10182+
error(node, Diagnostics.Constructor_of_type_0_is_protected_and_only_accessible_within_class_1, signatureToString(signature), typeToString(declaringClass));
10183+
}
10184+
return false;
10185+
}
10186+
10187+
return true;
10188+
}
10189+
1012610190
function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[]): Signature {
1012710191
const tagType = checkExpression(node.tag);
1012810192
const apparentType = getApparentType(tagType);
@@ -12059,7 +12123,7 @@ namespace ts {
1205912123
error(o.name, Diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient);
1206012124
}
1206112125
else if (deviation & (NodeFlags.Private | NodeFlags.Protected)) {
12062-
error(o.name, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected);
12126+
error(o.name || o, Diagnostics.Overload_signatures_must_all_be_public_private_or_protected);
1206312127
}
1206412128
else if (deviation & NodeFlags.Abstract) {
1206512129
error(o.name, Diagnostics.Overload_signatures_must_all_be_abstract_or_not_abstract);
@@ -13928,6 +13992,7 @@ namespace ts {
1392813992
if (baseTypes.length && produceDiagnostics) {
1392913993
const baseType = baseTypes[0];
1393013994
const staticBaseType = getBaseConstructorTypeOfClass(type);
13995+
checkBaseTypeAccessibility(staticBaseType, baseTypeNode);
1393113996
checkSourceElement(baseTypeNode.expression);
1393213997
if (baseTypeNode.typeArguments) {
1393313998
forEach(baseTypeNode.typeArguments, checkSourceElement);
@@ -13983,6 +14048,16 @@ namespace ts {
1398314048
}
1398414049
}
1398514050

14051+
function checkBaseTypeAccessibility(type: ObjectType, node: ExpressionWithTypeArguments) {
14052+
const signatures = getSignaturesOfType(type, SignatureKind.Construct);
14053+
if (signatures.length) {
14054+
const declaration = signatures[0].declaration;
14055+
if (declaration && declaration.flags & NodeFlags.Private) {
14056+
error(node, Diagnostics.Cannot_extend_private_class_0, (<Identifier>node.expression).text);
14057+
}
14058+
}
14059+
}
14060+
1398614061
function getTargetSymbol(s: Symbol) {
1398714062
// if symbol is instantiated its flags are not copied from the 'target'
1398814063
// so we'll need to get back original 'target' symbol to work with correct set of flags
@@ -16348,12 +16423,6 @@ namespace ts {
1634816423
if (flags & NodeFlags.Abstract) {
1634916424
return grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "abstract");
1635016425
}
16351-
else if (flags & NodeFlags.Protected) {
16352-
return grammarErrorOnNode(lastProtected, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "protected");
16353-
}
16354-
else if (flags & NodeFlags.Private) {
16355-
return grammarErrorOnNode(lastPrivate, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "private");
16356-
}
1635716426
else if (flags & NodeFlags.Async) {
1635816427
return grammarErrorOnNode(lastAsync, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "async");
1635916428
}

src/compiler/diagnosticMessages.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,22 @@
18231823
"category": "Error",
18241824
"code": 2671
18251825
},
1826+
"Cannot assign a '{0}' constructor type to a '{1}' constructor type.": {
1827+
"category": "Error",
1828+
"code": 2672
1829+
},
1830+
"Constructor of type '{0}' is private and only accessible within class '{1}'.": {
1831+
"category": "Error",
1832+
"code": 2673
1833+
},
1834+
"Constructor of type '{0}' is protected and only accessible within class '{1}'.": {
1835+
"category": "Error",
1836+
"code": 2674
1837+
},
1838+
"Cannot extend private class '{0}'.": {
1839+
"category": "Error",
1840+
"code": 2675
1841+
},
18261842
"Import declaration '{0}' is using private name '{1}'.": {
18271843
"category": "Error",
18281844
"code": 4000

0 commit comments

Comments
 (0)