Skip to content

Commit e1d5bdf

Browse files
committed
String and numeric literal initializes in ambient const declarations
1 parent a8e0042 commit e1d5bdf

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

src/compiler/checker.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,6 +2015,10 @@ namespace ts {
20152015
isExternalModuleAugmentation(node.parent.parent);
20162016
}
20172017

2018+
function literalTypeToString(type: LiteralType) {
2019+
return type.flags & TypeFlags.StringLiteral ? `"${escapeString((<LiteralType>type).text)}"` : (<LiteralType>type).text;
2020+
}
2021+
20182022
function getSymbolDisplayBuilder(): SymbolDisplayBuilder {
20192023

20202024
function getNameOfSymbol(symbol: Symbol): string {
@@ -2190,11 +2194,8 @@ namespace ts {
21902194
else if (type.flags & TypeFlags.Anonymous) {
21912195
writeAnonymousType(<ObjectType>type, nextFlags);
21922196
}
2193-
else if (type.flags & TypeFlags.StringLiteral) {
2194-
writer.writeStringLiteral(`"${escapeString((<LiteralType>type).text)}"`);
2195-
}
2196-
else if (type.flags & TypeFlags.NumberLiteral) {
2197-
writer.writeStringLiteral((<LiteralType>type).text);
2197+
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
2198+
writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
21982199
}
21992200
else {
22002201
// Should never get here
@@ -19015,6 +19016,19 @@ namespace ts {
1901519016
return undefined;
1901619017
}
1901719018

19019+
function isLiteralConstDeclaration(node: VariableDeclaration): boolean {
19020+
if (isConst(node)) {
19021+
const type = getTypeOfSymbol(getSymbolOfNode(node));
19022+
return !!(type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral);
19023+
}
19024+
return false;
19025+
}
19026+
19027+
function writeLiteralConstValue(node: VariableDeclaration, writer: SymbolWriter) {
19028+
const type = getTypeOfSymbol(getSymbolOfNode(node));
19029+
writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
19030+
}
19031+
1901819032
function createResolver(): EmitResolver {
1901919033
// this variable and functions that use it are deliberately moved here from the outer scope
1902019034
// to avoid scope pollution
@@ -19059,7 +19073,9 @@ namespace ts {
1905919073
isArgumentsLocalBinding,
1906019074
getExternalModuleFileFromDeclaration,
1906119075
getTypeReferenceDirectivesForEntityName,
19062-
getTypeReferenceDirectivesForSymbol
19076+
getTypeReferenceDirectivesForSymbol,
19077+
isLiteralConstDeclaration,
19078+
writeLiteralConstValue
1906319079
};
1906419080

1906519081
// defined here to avoid outer scope pollution
@@ -20205,10 +20221,29 @@ namespace ts {
2020520221
}
2020620222
}
2020720223

20224+
function isStringOrNumberLiteralExpression(expr: Expression) {
20225+
return expr.kind === SyntaxKind.StringLiteral || expr.kind === SyntaxKind.NumericLiteral ||
20226+
expr.kind === SyntaxKind.PrefixUnaryExpression && (<PrefixUnaryExpression>expr).operator === SyntaxKind.MinusToken &&
20227+
(<PrefixUnaryExpression>expr).operand.kind === SyntaxKind.NumericLiteral;
20228+
}
20229+
2020820230
function checkGrammarVariableDeclaration(node: VariableDeclaration) {
2020920231
if (node.parent.parent.kind !== SyntaxKind.ForInStatement && node.parent.parent.kind !== SyntaxKind.ForOfStatement) {
2021020232
if (isInAmbientContext(node)) {
2021120233
if (node.initializer) {
20234+
if (isConst(node) && !node.type) {
20235+
if (!isStringOrNumberLiteralExpression(node.initializer)) {
20236+
return grammarErrorOnNode(node.initializer, Diagnostics.A_const_initializer_in_an_ambient_context_must_be_a_string_or_numeric_literal);
20237+
}
20238+
}
20239+
else {
20240+
// Error on equals token which immediate precedes the initializer
20241+
const equalsTokenLength = "=".length;
20242+
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength,
20243+
equalsTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
20244+
}
20245+
}
20246+
if (node.initializer && !(isConst(node) && isStringOrNumberLiteralExpression(node.initializer))) {
2021220247
// Error on equals token which immediate precedes the initializer
2021320248
const equalsTokenLength = "=".length;
2021420249
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - equalsTokenLength,

src/compiler/declarationEmitter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,10 @@ namespace ts {
11421142
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) {
11431143
emitTypeOfVariableDeclarationFromTypeLiteral(node);
11441144
}
1145+
else if (resolver.isLiteralConstDeclaration(node)) {
1146+
write(" = ");
1147+
resolver.writeLiteralConstValue(node, writer);
1148+
}
11451149
else if (!hasModifier(node, ModifierFlags.Private)) {
11461150
writeTypeOfDeclaration(node, node.type, getVariableDeclarationTypeVisibilityError);
11471151
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,10 @@
819819
"category": "Error",
820820
"code": 1253
821821
},
822+
"A 'const' initializer in an ambient context must be a string or numeric literal.": {
823+
"category": "Error",
824+
"code": 1254
825+
},
822826
"'with' statements are not allowed in an async function block.": {
823827
"category": "Error",
824828
"code": 1300

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,8 @@ namespace ts {
21562156
getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): SourceFile;
21572157
getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): string[];
21582158
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[];
2159+
isLiteralConstDeclaration(node: VariableDeclaration): boolean;
2160+
writeLiteralConstValue(node: VariableDeclaration, writer: SymbolWriter): void;
21592161
}
21602162

21612163
export const enum SymbolFlags {

0 commit comments

Comments
 (0)