New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow dynamic names in types #15473

Merged
merged 65 commits into from Nov 16, 2017
Commits
Jump to file or symbol
Failed to load files and symbols.
+1,277 −9
Diff settings

Always

Just for now

Next

Allow some dynamic names in types

  • Loading branch information...
rbuckton committed Apr 29, 2017
commit d8cb3c628af07ae522e2955f54c5fc0e7a9b7dd4
Copy path View file
@@ -959,7 +959,7 @@ namespace ts {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
if (result = getSymbol(getSymbolOfNode(location).members, name, meaning & SymbolFlags.Type)) {
if (result = getSymbol(getMembersOfSymbol(getSymbolOfNode(location)), name, meaning & SymbolFlags.Type)) {
if (!isTypeParameterSymbolDeclaredInContainer(result, location)) {
// ignore type parameters not declared in this container
result = undefined;
@@ -2895,6 +2895,9 @@ namespace ts {
}
function getNameOfSymbol(symbol: Symbol): string {
if (symbol.flags & SymbolFlags.Dynamic) {
return unescapeIdentifier(symbol.name);
}
if (symbol.declarations && symbol.declarations.length) {
const declaration = symbol.declarations[0];
if (declaration.name) {
@@ -5133,7 +5136,7 @@ namespace ts {
function resolveDeclaredMembers(type: InterfaceType): InterfaceTypeWithDeclaredMembers {
if (!(<InterfaceTypeWithDeclaredMembers>type).declaredProperties) {
const symbol = type.symbol;
(<InterfaceTypeWithDeclaredMembers>type).declaredProperties = getNamedMembers(symbol.members);
(<InterfaceTypeWithDeclaredMembers>type).declaredProperties = getNamedMembers(getMembersOfSymbol(symbol));
(<InterfaceTypeWithDeclaredMembers>type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members.get("__call"));
(<InterfaceTypeWithDeclaredMembers>type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members.get("__new"));
(<InterfaceTypeWithDeclaredMembers>type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String);
@@ -5142,6 +5145,137 @@ namespace ts {
return <InterfaceTypeWithDeclaredMembers>type;
}
function getMembersOfSymbol(symbol: Symbol) {
const links = getSymbolLinks(symbol);
if (!links.resolvedMembers) {
links.resolvedMembers = emptySymbols;
const dynamicMembers = getDynamicMembersOfSymbol(symbol);
if (!dynamicMembers || dynamicMembers.size === 0) {
return links.resolvedMembers = symbol.members || emptySymbols;
}
if (!symbol.members || symbol.members.size === 0) {
return links.resolvedMembers = dynamicMembers;
}
const resolvedMembers = createMap<Symbol>();
mergeSymbolTable(resolvedMembers, symbol.members);
mergeSymbolTable(resolvedMembers, dynamicMembers);
return links.resolvedMembers = resolvedMembers;
}
return links.resolvedMembers;
}
function getDynamicMembersOfSymbol(symbol: Symbol) {
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral)) {
const links = getSymbolLinks(symbol);
if (!links.dynamicMembers) {
const members = createMap<Symbol>();
for (const decl of symbol.declarations) {
resolveDynamicMembersOfSymbol(decl, members);
}
links.dynamicMembers = members;
}
return links.dynamicMembers;
}
}
function resolveDynamicMembersOfSymbol(node: Declaration, symbolTable: SymbolTable) {
switch (node.kind) {
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.TypeLiteral:
resolveDynamicMembersOfClassOrInterfaceOrTypeLiteralNode(<ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode>node, symbolTable);
break;
case SyntaxKind.ObjectLiteralExpression:
resolveDynamicMembersOfObjectLiteralExpression(<ObjectLiteralExpression>node, symbolTable);
break;
}
}
function resolveDynamicMembersOfClassOrInterfaceOrTypeLiteralNode(node: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, symbolTable: SymbolTable) {
for (const member of node.members) {
if (member.name && isComputedPropertyName(member.name) && isEntityNameExpression(member.name.expression)) {
bindDynamicMember(symbolTable, node.symbol, member);

This comment has been minimized.

@sandersn

sandersn May 2, 2017

Member

it would be simpler to have a third parameter symbol and only one resolveDynamicMembersOfNode(members: NodeArray<ClassElement | TypeElement | ObjectLiteralElementLike>, symbols: Symbol[], symbolTable: SymbolTable). Then resolveDynamicMembersOfSymbol could pull off the properties of node in each case.

}
}
}
function resolveDynamicMembersOfObjectLiteralExpression(node: ObjectLiteralExpression, symbolTable: SymbolTable) {
for (const member of node.properties) {
if (member.name && isComputedPropertyName(member.name) && isEntityNameExpression(member.name.expression)) {
bindDynamicMember(symbolTable, node.symbol, member);
}
}
}
function bindDynamicMember(symbolTable: SymbolTable, parent: Symbol, member: ClassElement | TypeElement | ObjectLiteralElement) {
const links = getNodeLinks(member);
if (!links.resolvedSymbol) {
switch (member.kind) {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return resolveDynamicMember(symbolTable, parent, member,
SymbolFlags.Property | ((<PropertyDeclaration>member).questionToken ? SymbolFlags.Optional : SymbolFlags.None),
SymbolFlags.PropertyExcludes);
case SyntaxKind.PropertyAssignment:
return resolveDynamicMember(symbolTable, parent, member, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return resolveDynamicMember(symbolTable, parent, member,
SymbolFlags.Method | ((<MethodDeclaration>member).questionToken ? SymbolFlags.Optional : SymbolFlags.None),
isObjectLiteralMethod(member) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes);
case SyntaxKind.GetAccessor:
return resolveDynamicMember(symbolTable, parent, member, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes);
case SyntaxKind.SetAccessor:
return resolveDynamicMember(symbolTable, parent, member, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes);
}
}
return links.resolvedSymbol;
}
function resolveDynamicMember(symbolTable: SymbolTable, parent: Symbol, member: ClassElement | TypeElement | ObjectLiteralElement, includes: SymbolFlags, excludes: SymbolFlags) {
Debug.assert(isComputedPropertyName(member.name));
const nameType = checkComputedPropertyName(<ComputedPropertyName>member.name);
if (nameType.flags & TypeFlags.StringOrNumberLiteral) {
// TODO(rbuckton): ESSymbolLiteral
const memberName = escapeIdentifier((<LiteralType>nameType).text);
let symbol = symbolTable.get(memberName);
if (!symbol) {
symbolTable.set(memberName, symbol = createSymbol(SymbolFlags.Dynamic, memberName));
}
const staticMember = parent.members && parent.members.get(memberName);
if (symbol.flags & excludes || staticMember) {
const declarations = staticMember ? concatenate(staticMember.declarations, symbol.declarations) : symbol.declarations;
forEach(declarations, declaration => {
error(declaration.name || declaration, Diagnostics.Duplicate_identifier_0, memberName);
});
error(member.name || member, Diagnostics.Duplicate_identifier_0, memberName);
symbol = createSymbol(SymbolFlags.Dynamic, memberName);
}
addDeclarationToSymbol(symbol, member, includes);
symbol.parent = parent;
return symbol;
}
return getNodeLinks(member).resolvedSymbol = member.symbol || unknownSymbol;
}
function addDeclarationToSymbol(symbol: Symbol, member: ClassElement | TypeElement | ObjectLiteralElement, symbolFlags: SymbolFlags) {
symbol.flags |= symbolFlags;
getNodeLinks(member).resolvedSymbol = symbol;
if (!symbol.declarations) {
symbol.declarations = [member];
}
else {
symbol.declarations.push(member);
}
if (symbolFlags & SymbolFlags.Value) {
const valueDeclaration = symbol.valueDeclaration;
if (!valueDeclaration || valueDeclaration.kind !== member.kind) {
symbol.valueDeclaration = member;
}
}
}
function getTypeWithThisArgument(type: Type, thisArgument?: Type): Type {
if (getObjectFlags(type) & ObjectFlags.Reference) {
const target = (<TypeReference>type).target;
@@ -5165,7 +5299,7 @@ namespace ts {
let numberIndexInfo: IndexInfo;
if (rangeEquals(typeParameters, typeArguments, 0, typeParameters.length)) {
mapper = identityMapper;
members = source.symbol ? source.symbol.members : createSymbolTable(source.declaredProperties);
members = source.symbol ? getMembersOfSymbol(source.symbol) : createSymbolTable(source.declaredProperties);
callSignatures = source.declaredCallSignatures;
constructSignatures = source.declaredConstructSignatures;
stringIndexInfo = source.declaredStringIndexInfo;
@@ -5425,7 +5559,7 @@ namespace ts {
setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
else if (symbol.flags & SymbolFlags.TypeLiteral) {
const members = symbol.members;
const members = getMembersOfSymbol(symbol);
const callSignatures = getSignaturesOfSymbol(members.get("__call"));
const constructSignatures = getSignaturesOfSymbol(members.get("__new"));
const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String);
@@ -7396,7 +7530,7 @@ namespace ts {
if (!links.resolvedType) {
// Deferred resolution of members is handled by resolveObjectTypeMembers
const aliasSymbol = getAliasSymbolForTypeNode(node);
if (node.symbol.members.size === 0 && !aliasSymbol) {
if (getMembersOfSymbol(node.symbol).size === 0 && !aliasSymbol) {
links.resolvedType = emptyTypeLiteralType;
}
else {
@@ -15623,7 +15757,7 @@ namespace ts {
function getInferredClassType(symbol: Symbol) {
const links = getSymbolLinks(symbol);
if (!links.inferredClassType) {
links.inferredClassType = createAnonymousType(symbol, symbol.members, emptyArray, emptyArray, /*stringIndexType*/ undefined, /*numberIndexType*/ undefined);
links.inferredClassType = createAnonymousType(symbol, getMembersOfSymbol(symbol), emptyArray, emptyArray, /*stringIndexType*/ undefined, /*numberIndexType*/ undefined);
}
return links.inferredClassType;
}
@@ -21741,7 +21875,7 @@ namespace ts {
// (type parameters of classDeclaration/classExpression and interface are in member property of the symbol.
// Note: that the memberFlags come from previous iteration.
if (!(memberFlags & ModifierFlags.Static)) {
copySymbols(getSymbolOfNode(location).members, meaning & SymbolFlags.Type);
copySymbols(getMembersOfSymbol(getSymbolOfNode(location)), meaning & SymbolFlags.Type);
}
break;
case SyntaxKind.FunctionExpression:
@@ -23739,7 +23873,10 @@ namespace ts {
function checkGrammarForNonSymbolComputedProperty(node: DeclarationName, message: DiagnosticMessage) {
if (isDynamicName(node)) {
return grammarErrorOnNode(node, message);
if (!isEntityNameExpression((<ComputedPropertyName>node).expression) ||
(checkExpressionCached((<ComputedPropertyName>node).expression).flags & TypeFlags.StringOrNumberLiteral) === 0) {
return grammarErrorOnNode(node, message);
}
}
}
Copy path View file
@@ -2775,6 +2775,7 @@ namespace ts {
ExportStar = 1 << 25, // Export * declaration
Optional = 1 << 26, // Optional property
Transient = 1 << 27, // Transient symbol (created during type check)
Dynamic = 1 << 28, // Dynamically resolved symbol from computed property
Enum = RegularEnum | ConstEnum,
Variable = FunctionScopedVariable | BlockScopedVariable,
@@ -2869,6 +2870,8 @@ namespace ts {
isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration
bindingElement?: BindingElement; // Binding element associated with property symbol
exportsSomeValue?: boolean; // True if module exports some value (not just types)
dynamicMembers?: SymbolTable; // Dynamic members with literal names resolved during check
resolvedMembers?: SymbolTable;
}
/* @internal */
@@ -68,6 +68,9 @@ namespace ts {
transformers: {
before: [replaceUndefinedWithVoid0],
after: [replaceIdentifiersNamedOldNameWithNewName]
},
compilerOptions: {
newLine: ts.NewLineKind.CarriageReturnLineFeed
}
}).outputText;
});
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.