Skip to content

Commit b4ea700

Browse files
authored
Merge pull request #15473 from Microsoft/dynamicNames
Allow dynamic names in types
2 parents 062e759 + ccba128 commit b4ea700

File tree

171 files changed

+10267
-1174
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

171 files changed

+10267
-1174
lines changed

src/compiler/binder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1680,7 +1680,7 @@ namespace ts {
16801680

16811681
function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: __String) {
16821682
const symbol = createSymbol(symbolFlags, name);
1683-
if (symbolFlags & SymbolFlags.EnumMember) {
1683+
if (symbolFlags & (SymbolFlags.EnumMember | SymbolFlags.ClassMember)) {
16841684
symbol.parent = container.symbol;
16851685
}
16861686
addDeclarationToSymbol(symbol, node, symbolFlags);

src/compiler/checker.ts

+525-114
Large diffs are not rendered by default.

src/compiler/declarationEmitter.ts

+107-11
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ namespace ts {
190190
const writer = <EmitTextWriterWithSymbolWriter>createTextWriter(newLine);
191191
writer.trackSymbol = trackSymbol;
192192
writer.reportInaccessibleThisError = reportInaccessibleThisError;
193+
writer.reportInaccessibleUniqueSymbolError = reportInaccessibleUniqueSymbolError;
193194
writer.reportPrivateInBaseOfClassExpression = reportPrivateInBaseOfClassExpression;
194195
writer.writeKeyword = writer.write;
195196
writer.writeOperator = writer.write;
@@ -322,11 +323,21 @@ namespace ts {
322323
}
323324
}
324325

326+
function reportInaccessibleUniqueSymbolError() {
327+
if (errorNameNode) {
328+
reportedDeclarationError = true;
329+
emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary,
330+
declarationNameToString(errorNameNode),
331+
"unique symbol"));
332+
}
333+
}
334+
325335
function reportInaccessibleThisError() {
326336
if (errorNameNode) {
327337
reportedDeclarationError = true;
328-
emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_this_type_A_type_annotation_is_necessary,
329-
declarationNameToString(errorNameNode)));
338+
emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary,
339+
declarationNameToString(errorNameNode),
340+
"this"));
330341
}
331342
}
332343

@@ -1227,7 +1238,7 @@ namespace ts {
12271238
}
12281239

12291240
function emitPropertyDeclaration(node: Declaration) {
1230-
if (hasDynamicName(node)) {
1241+
if (hasDynamicName(node) && !resolver.isLateBound(node)) {
12311242
return;
12321243
}
12331244

@@ -1246,10 +1257,8 @@ namespace ts {
12461257
emitBindingPattern(<BindingPattern>node.name);
12471258
}
12481259
else {
1249-
// If this node is a computed name, it can only be a symbol, because we've already skipped
1250-
// it if it's not a well known symbol. In that case, the text of the name will be exactly
1251-
// what we want, namely the name expression enclosed in brackets.
1252-
writeTextOfNode(currentText, node.name);
1260+
writeNameOfDeclaration(node, getVariableDeclarationTypeVisibilityError);
1261+
12531262
// If optional property emit ? but in the case of parameterProperty declaration with "?" indicating optional parameter for the constructor
12541263
// we don't want to emit property declaration with "?"
12551264
if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature ||
@@ -1387,7 +1396,7 @@ namespace ts {
13871396
}
13881397

13891398
function emitAccessorDeclaration(node: AccessorDeclaration) {
1390-
if (hasDynamicName(node)) {
1399+
if (hasDynamicName(node) && !resolver.isLateBound(node)) {
13911400
return;
13921401
}
13931402

@@ -1398,7 +1407,7 @@ namespace ts {
13981407
emitJsDocComments(accessors.getAccessor);
13991408
emitJsDocComments(accessors.setAccessor);
14001409
emitClassMemberDeclarationFlags(getModifierFlags(node) | (accessors.setAccessor ? 0 : ModifierFlags.Readonly));
1401-
writeTextOfNode(currentText, node.name);
1410+
writeNameOfDeclaration(node, getAccessorNameVisibilityError);
14021411
if (!hasModifier(node, ModifierFlags.Private)) {
14031412
accessorWithTypeAnnotation = node;
14041413
let type = getTypeAnnotationFromAccessor(node);
@@ -1426,6 +1435,37 @@ namespace ts {
14261435
}
14271436
}
14281437

1438+
function getAccessorNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) {
1439+
const diagnosticMessage = getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult);
1440+
return diagnosticMessage !== undefined ? {
1441+
diagnosticMessage,
1442+
errorNode: node,
1443+
typeName: node.name
1444+
} : undefined;
1445+
}
1446+
1447+
function getAccessorNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
1448+
if (hasModifier(node, ModifierFlags.Static)) {
1449+
return symbolAccessibilityResult.errorModuleName ?
1450+
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
1451+
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
1452+
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
1453+
Diagnostics.Public_static_property_0_of_exported_class_has_or_is_using_private_name_1;
1454+
}
1455+
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
1456+
return symbolAccessibilityResult.errorModuleName ?
1457+
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
1458+
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
1459+
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
1460+
Diagnostics.Public_property_0_of_exported_class_has_or_is_using_private_name_1;
1461+
}
1462+
else {
1463+
return symbolAccessibilityResult.errorModuleName ?
1464+
Diagnostics.Property_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
1465+
Diagnostics.Property_0_of_exported_interface_has_or_is_using_private_name_1;
1466+
}
1467+
}
1468+
14291469
function getAccessorDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
14301470
let diagnosticMessage: DiagnosticMessage;
14311471
if (accessorWithTypeAnnotation.kind === SyntaxKind.SetAccessor) {
@@ -1467,7 +1507,7 @@ namespace ts {
14671507
}
14681508

14691509
function writeFunctionDeclaration(node: FunctionLikeDeclaration) {
1470-
if (hasDynamicName(node)) {
1510+
if (hasDynamicName(node) && !resolver.isLateBound(node)) {
14711511
return;
14721512
}
14731513

@@ -1489,13 +1529,69 @@ namespace ts {
14891529
write("constructor");
14901530
}
14911531
else {
1492-
writeTextOfNode(currentText, node.name);
1532+
writeNameOfDeclaration(node, getMethodNameVisibilityError);
14931533
if (hasQuestionToken(node)) {
14941534
write("?");
14951535
}
14961536
}
14971537
emitSignatureDeclaration(node);
14981538
}
1539+
1540+
function getMethodNameVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic {
1541+
const diagnosticMessage = getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult);
1542+
return diagnosticMessage !== undefined ? {
1543+
diagnosticMessage,
1544+
errorNode: node,
1545+
typeName: node.name
1546+
} : undefined;
1547+
}
1548+
1549+
function getMethodNameVisibilityDiagnosticMessage(symbolAccessibilityResult: SymbolAccessibilityResult) {
1550+
if (hasModifier(node, ModifierFlags.Static)) {
1551+
return symbolAccessibilityResult.errorModuleName ?
1552+
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
1553+
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
1554+
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
1555+
Diagnostics.Public_static_method_0_of_exported_class_has_or_is_using_private_name_1;
1556+
}
1557+
else if (node.parent.kind === SyntaxKind.ClassDeclaration) {
1558+
return symbolAccessibilityResult.errorModuleName ?
1559+
symbolAccessibilityResult.accessibility === SymbolAccessibility.CannotBeNamed ?
1560+
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named :
1561+
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_name_1_from_private_module_2 :
1562+
Diagnostics.Public_method_0_of_exported_class_has_or_is_using_private_name_1;
1563+
}
1564+
else {
1565+
return symbolAccessibilityResult.errorModuleName ?
1566+
Diagnostics.Method_0_of_exported_interface_has_or_is_using_name_1_from_private_module_2 :
1567+
Diagnostics.Method_0_of_exported_interface_has_or_is_using_private_name_1;
1568+
}
1569+
}
1570+
}
1571+
1572+
function writeNameOfDeclaration(node: NamedDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
1573+
if (hasDynamicName(node)) {
1574+
// If this node has a dynamic name, it can only be an identifier or property access because
1575+
// we've already skipped it otherwise.
1576+
Debug.assert(resolver.isLateBound(node));
1577+
1578+
writeLateBoundNameOfDeclaration(node as LateBoundDeclaration, getSymbolAccessibilityDiagnostic);
1579+
}
1580+
else {
1581+
// If this node is a computed name, it can only be a symbol, because we've already skipped
1582+
// it if it's not a well known symbol. In that case, the text of the name will be exactly
1583+
// what we want, namely the name expression enclosed in brackets.
1584+
writeTextOfNode(currentText, node.name);
1585+
}
1586+
}
1587+
1588+
function writeLateBoundNameOfDeclaration(node: LateBoundDeclaration, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
1589+
writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic;
1590+
const entityName = node.name.expression;
1591+
const visibilityResult = resolver.isEntityNameVisible(entityName, enclosingDeclaration);
1592+
handleSymbolAccessibilityError(visibilityResult);
1593+
recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForEntityName(entityName));
1594+
writeTextOfNode(currentText, node.name);
14991595
}
15001596

15011597
function emitSignatureDeclarationWithJsDocComments(node: SignatureDeclaration) {

src/compiler/diagnosticMessages.json

+71-6
Original file line numberDiff line numberDiff line change
@@ -491,23 +491,23 @@
491491
"category": "Error",
492492
"code": 1164
493493
},
494-
"A computed property name in an ambient context must directly refer to a built-in symbol.": {
494+
"A computed property name in an ambient context must refer to an expression whose type is a literal type or a 'unique symbol' type.": {
495495
"category": "Error",
496496
"code": 1165
497497
},
498-
"A computed property name in a class property declaration must directly refer to a built-in symbol.": {
498+
"A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type.": {
499499
"category": "Error",
500500
"code": 1166
501501
},
502-
"A computed property name in a method overload must directly refer to a built-in symbol.": {
502+
"A computed property name in a method overload must refer to an expression whose type is a literal type or a 'unique symbol' type.": {
503503
"category": "Error",
504504
"code": 1168
505505
},
506-
"A computed property name in an interface must directly refer to a built-in symbol.": {
506+
"A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.": {
507507
"category": "Error",
508508
"code": 1169
509509
},
510-
"A computed property name in a type literal must directly refer to a built-in symbol.": {
510+
"A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.": {
511511
"category": "Error",
512512
"code": 1170
513513
},
@@ -911,6 +911,30 @@
911911
"category": "Error",
912912
"code": 1329
913913
},
914+
"A property of an interface or type literal whose type is a 'unique symbol' type must be 'readonly'.": {
915+
"category": "Error",
916+
"code": 1330
917+
},
918+
"A property of a class whose type is a 'unique symbol' type must be both 'static' and 'readonly'.": {
919+
"category": "Error",
920+
"code": 1331
921+
},
922+
"A variable whose type is a 'unique symbol' type must be 'const'.": {
923+
"category": "Error",
924+
"code": 1332
925+
},
926+
"'unique symbol' types may not be used on a variable declaration with a binding name.": {
927+
"category": "Error",
928+
"code": 1333
929+
},
930+
"'unique symbol' types are only allowed on variables in a variable statement.": {
931+
"category": "Error",
932+
"code": 1334
933+
},
934+
"'unique symbol' types are not allowed here.": {
935+
"category": "Error",
936+
"code": 1335
937+
},
914938

915939
"Duplicate identifier '{0}'.": {
916940
"category": "Error",
@@ -1780,7 +1804,7 @@
17801804
"category": "Error",
17811805
"code": 2526
17821806
},
1783-
"The inferred type of '{0}' references an inaccessible 'this' type. A type annotation is necessary.": {
1807+
"The inferred type of '{0}' references an inaccessible '{1}' type. A type annotation is necessary.": {
17841808
"category": "Error",
17851809
"code": 2527
17861810
},
@@ -2228,6 +2252,14 @@
22282252
"category": "Error",
22292253
"code": 2716
22302254
},
2255+
"Subsequent property declarations must have the same type. Property '{0}' has type '{1}' at {2}, but here has type '{3}'.": {
2256+
"category": "Error",
2257+
"code": 2717
2258+
},
2259+
"Duplicate declaration '{0}'.": {
2260+
"category": "Error",
2261+
"code": 2718
2262+
},
22312263

22322264
"Import declaration '{0}' is using private name '{1}'.": {
22332265
"category": "Error",
@@ -2530,6 +2562,39 @@
25302562
"code": 4094
25312563
},
25322564

2565+
"Public static method '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": {
2566+
"category": "Error",
2567+
"code": 4095
2568+
},
2569+
"Public static method '{0}' of exported class has or is using name '{1}' from private module '{2}'.": {
2570+
"category": "Error",
2571+
"code": 4096
2572+
},
2573+
"Public static method '{0}' of exported class has or is using private name '{1}'.": {
2574+
"category": "Error",
2575+
"code": 4097
2576+
},
2577+
"Public method '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": {
2578+
"category": "Error",
2579+
"code": 4098
2580+
},
2581+
"Public method '{0}' of exported class has or is using name '{1}' from private module '{2}'.": {
2582+
"category": "Error",
2583+
"code": 4099
2584+
},
2585+
"Public method '{0}' of exported class has or is using private name '{1}'.": {
2586+
"category": "Error",
2587+
"code": 4100
2588+
},
2589+
"Method '{0}' of exported interface has or is using name '{1}' from private module '{2}'.": {
2590+
"category": "Error",
2591+
"code": 4101
2592+
},
2593+
"Method '{0}' of exported interface has or is using private name '{1}'.": {
2594+
"category": "Error",
2595+
"code": 4102
2596+
},
2597+
25332598
"The current host does not support the '{0}' option.": {
25342599
"category": "Error",
25352600
"code": 5001

src/compiler/factory.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -736,15 +736,17 @@ namespace ts {
736736
return <ThisTypeNode>createSynthesizedNode(SyntaxKind.ThisType);
737737
}
738738

739-
export function createTypeOperatorNode(type: TypeNode) {
739+
export function createTypeOperatorNode(type: TypeNode): TypeOperatorNode;
740+
export function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword, type: TypeNode): TypeOperatorNode;
741+
export function createTypeOperatorNode(operatorOrType: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | TypeNode, type?: TypeNode) {
740742
const node = createSynthesizedNode(SyntaxKind.TypeOperator) as TypeOperatorNode;
741-
node.operator = SyntaxKind.KeyOfKeyword;
742-
node.type = parenthesizeElementTypeMember(type);
743+
node.operator = typeof operatorOrType === "number" ? operatorOrType : SyntaxKind.KeyOfKeyword;
744+
node.type = parenthesizeElementTypeMember(typeof operatorOrType === "number" ? type : operatorOrType);
743745
return node;
744746
}
745747

746748
export function updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode) {
747-
return node.type !== type ? updateNode(createTypeOperatorNode(type), node) : node;
749+
return node.type !== type ? updateNode(createTypeOperatorNode(node.operator, type), node) : node;
748750
}
749751

750752
export function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode) {

src/compiler/parser.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -2665,8 +2665,8 @@ namespace ts {
26652665
case SyntaxKind.AnyKeyword:
26662666
case SyntaxKind.StringKeyword:
26672667
case SyntaxKind.NumberKeyword:
2668-
case SyntaxKind.BooleanKeyword:
26692668
case SyntaxKind.SymbolKeyword:
2669+
case SyntaxKind.BooleanKeyword:
26702670
case SyntaxKind.UndefinedKeyword:
26712671
case SyntaxKind.NeverKeyword:
26722672
case SyntaxKind.ObjectKeyword:
@@ -2720,6 +2720,7 @@ namespace ts {
27202720
case SyntaxKind.NumberKeyword:
27212721
case SyntaxKind.BooleanKeyword:
27222722
case SyntaxKind.SymbolKeyword:
2723+
case SyntaxKind.UniqueKeyword:
27232724
case SyntaxKind.VoidKeyword:
27242725
case SyntaxKind.UndefinedKeyword:
27252726
case SyntaxKind.NullKeyword:
@@ -2805,7 +2806,7 @@ namespace ts {
28052806
return finishNode(postfix);
28062807
}
28072808

2808-
function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword) {
2809+
function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword) {
28092810
const node = <TypeOperatorNode>createNode(SyntaxKind.TypeOperator);
28102811
parseExpected(operator);
28112812
node.operator = operator;
@@ -2814,9 +2815,11 @@ namespace ts {
28142815
}
28152816

28162817
function parseTypeOperatorOrHigher(): TypeNode {
2817-
switch (token()) {
2818+
const operator = token();
2819+
switch (operator) {
28182820
case SyntaxKind.KeyOfKeyword:
2819-
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
2821+
case SyntaxKind.UniqueKeyword:
2822+
return parseTypeOperator(operator);
28202823
case SyntaxKind.DotDotDotToken: {
28212824
const result = createNode(SyntaxKind.JSDocVariadicType) as JSDocVariadicType;
28222825
nextToken();

0 commit comments

Comments
 (0)