Skip to content

Commit fac1bf6

Browse files
committed
Disallow assigments to exported variables from external modules
1 parent b6f43e6 commit fac1bf6

File tree

1 file changed

+37
-17
lines changed

1 file changed

+37
-17
lines changed

src/compiler/checker.ts

+37-17
Original file line numberDiff line numberDiff line change
@@ -10400,25 +10400,44 @@ namespace ts {
1040010400
return true;
1040110401
}
1040210402

10403-
function isReferenceWithinOwnConstructor(expr: Expression, symbol: Symbol): boolean {
10404-
// Allow assignments to readonly properties or methods (but not readonly accessors) within constructors
10405-
// of the same class declaration.
10406-
if ((expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) &&
10407-
(expr as PropertyAccessExpression | ElementAccessExpression).expression.kind === SyntaxKind.ThisKeyword &&
10408-
symbol.flags & (SymbolFlags.Property | SymbolFlags.Method)) {
10409-
const func = getContainingFunction(expr);
10410-
return func && func.kind === SyntaxKind.Constructor && func.parent === symbol.valueDeclaration.parent;
10411-
}
10412-
}
10413-
1041410403
function isReadonlySymbol(symbol: Symbol): boolean {
1041510404
return symbol.flags & SymbolFlags.Property && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Readonly) !== 0;
1041610405
}
1041710406

10418-
function isConstantSymbol(symbol: Symbol): boolean {
10419-
return symbol === undefinedSymbol ||
10420-
symbol.flags & SymbolFlags.Variable && (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Const) !== 0 ||
10421-
symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor);
10407+
function isReferenceToConstant(expr: Expression, symbol: Symbol): boolean {
10408+
if (symbol.flags & SymbolFlags.Variable) {
10409+
// A variable declared with 'const' is considered constant.
10410+
if (getDeclarationFlagsFromSymbol(symbol) & NodeFlags.Const) {
10411+
return true;
10412+
}
10413+
// An exported variable declared in an external module and accessed through a property
10414+
// or element access is considered constant.
10415+
if (expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) {
10416+
if (symbol.parent && symbol.parent.flags & SymbolFlags.ValueModule) {
10417+
const declaration = symbol.parent.valueDeclaration;
10418+
return declaration && (declaration.kind === SyntaxKind.SourceFile ||
10419+
declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).name.kind === SyntaxKind.StringLiteral);
10420+
}
10421+
}
10422+
}
10423+
if (symbol.flags & SymbolFlags.Accessor) {
10424+
// An get accessor with no corresponding set accessor is considered constant.
10425+
return !(symbol.flags & SymbolFlags.SetAccessor);
10426+
}
10427+
return false;
10428+
}
10429+
10430+
function isReferenceToReadonlyProperty(expr: Expression, symbol: Symbol): boolean {
10431+
if (isReadonlySymbol(symbol)) {
10432+
// Allow assignments to readonly properties within constructors of the same class declaration.
10433+
if ((expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) &&
10434+
(expr as PropertyAccessExpression | ElementAccessExpression).expression.kind === SyntaxKind.ThisKeyword) {
10435+
const func = getContainingFunction(expr);
10436+
return !(func && func.kind === SyntaxKind.Constructor && func.parent === symbol.valueDeclaration.parent);
10437+
}
10438+
return true;
10439+
}
10440+
return false;
1042210441
}
1042310442

1042410443
function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, constantVariableMessage: DiagnosticMessage): boolean {
@@ -10435,11 +10454,12 @@ namespace ts {
1043510454
const symbol = getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol);
1043610455
if (symbol) {
1043710456
if (symbol !== unknownSymbol && symbol !== argumentsSymbol) {
10438-
if (symbol === undefinedSymbol || !(symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Method | SymbolFlags.Accessor))) {
10457+
if (symbol === undefinedSymbol ||
10458+
!(symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Method | SymbolFlags.Accessor))) {
1043910459
error(expr, invalidReferenceMessage);
1044010460
return false;
1044110461
}
10442-
if (isConstantSymbol(symbol) || (isReadonlySymbol(symbol) && !isReferenceWithinOwnConstructor(expr, symbol))) {
10462+
if (isReferenceToConstant(node, symbol) || isReferenceToReadonlyProperty(node, symbol)) {
1044310463
error(expr, constantVariableMessage);
1044410464
return false;
1044510465
}

0 commit comments

Comments
 (0)