diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d1ebe307294e7..f838831321b50 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2248,17 +2248,18 @@ namespace ts { return checkStrictModeIdentifier(node); case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: - if (currentFlow && isNarrowableReference(node)) { - node.flowNode = currentFlow; + const expr = node as PropertyAccessExpression | ElementAccessExpression; + if (currentFlow && isNarrowableReference(expr)) { + expr.flowNode = currentFlow; } - if (isSpecialPropertyDeclaration(node as PropertyAccessExpression)) { - bindSpecialPropertyDeclaration(node as PropertyAccessExpression); + if (isSpecialPropertyDeclaration(expr)) { + bindSpecialPropertyDeclaration(expr); } - if (isInJSFile(node) && + if (isInJSFile(expr) && file.commonJsModuleIndicator && - isModuleExportsPropertyAccessExpression(node as PropertyAccessExpression) && + isModuleExportsAccessExpression(expr) && !lookupSymbolForNameWorker(blockScopeContainer, "module" as __String)) { - declareSymbol(file.locals!, /*parent*/ undefined, (node as PropertyAccessExpression).expression as Identifier, + declareSymbol(file.locals!, /*parent*/ undefined, expr.expression, SymbolFlags.FunctionScopedVariable | SymbolFlags.ModuleExports, SymbolFlags.FunctionScopedVariableExcludes); } break; @@ -2266,22 +2267,22 @@ namespace ts { const specialKind = getAssignmentDeclarationKind(node as BinaryExpression); switch (specialKind) { case AssignmentDeclarationKind.ExportsProperty: - bindExportsPropertyAssignment(node as BinaryExpression); + bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression); break; case AssignmentDeclarationKind.ModuleExports: - bindModuleExportsAssignment(node as BinaryExpression); + bindModuleExportsAssignment(node as BindablePropertyAssignmentExpression); break; case AssignmentDeclarationKind.PrototypeProperty: - bindPrototypePropertyAssignment((node as BinaryExpression).left as PropertyAccessEntityNameExpression, node); + bindPrototypePropertyAssignment((node as BindableStaticPropertyAssignmentExpression).left, node); break; case AssignmentDeclarationKind.Prototype: - bindPrototypeAssignment(node as BinaryExpression); + bindPrototypeAssignment(node as BindableStaticPropertyAssignmentExpression); break; case AssignmentDeclarationKind.ThisProperty: - bindThisPropertyAssignment(node as BinaryExpression); + bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression); break; case AssignmentDeclarationKind.Property: - bindSpecialPropertyAssignment(node as BinaryExpression); + bindSpecialPropertyAssignment(node as BindablePropertyAssignmentExpression); break; case AssignmentDeclarationKind.None: // Nothing to do @@ -2556,14 +2557,13 @@ namespace ts { } } - function bindExportsPropertyAssignment(node: BinaryExpression) { + function bindExportsPropertyAssignment(node: BindableStaticPropertyAssignmentExpression) { // When we create a property via 'exports.foo = bar', the 'exports.foo' property access // expression is the declaration if (!setCommonJsModuleIndicator(node)) { return; } - const lhs = node.left as PropertyAccessEntityNameExpression; - const symbol = forEachIdentifierInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => { + const symbol = forEachIdentifierInEntityName(node.left.expression, /*parent*/ undefined, (id, symbol) => { if (symbol) { addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment); } @@ -2573,11 +2573,11 @@ namespace ts { const flags = isClassExpression(node.right) ? SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.Class : SymbolFlags.Property | SymbolFlags.ExportValue; - declareSymbol(symbol.exports!, symbol, lhs, flags, SymbolFlags.None); + declareSymbol(symbol.exports!, symbol, node.left, flags, SymbolFlags.None); } } - function bindModuleExportsAssignment(node: BinaryExpression) { + function bindModuleExportsAssignment(node: BindablePropertyAssignmentExpression) { // A common practice in node modules is to set 'export = module.exports = {}', this ensures that 'exports' // is still pointing to 'module.exports'. // We do not want to consider this as 'export=' since a module can have only one of these. @@ -2597,7 +2597,7 @@ namespace ts { declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None); } - function bindThisPropertyAssignment(node: BinaryExpression | PropertyAccessExpression) { + function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) { Debug.assert(isInJSFile(node)); const thisContainer = getThisContainer(node, /*includeArrowFunctions*/ false); switch (thisContainer.kind) { @@ -2607,7 +2607,7 @@ namespace ts { // For `f.prototype.m = function() { this.x = 0; }`, `this.x = 0` should modify `f`'s members, not the function expression. if (isBinaryExpression(thisContainer.parent) && thisContainer.parent.operatorToken.kind === SyntaxKind.EqualsToken) { const l = thisContainer.parent.left; - if (isPropertyAccessEntityNameExpression(l) && isPrototypeAccess(l.expression)) { + if (isBindableStaticAccessExpression(l) && isPrototypeAccess(l.expression)) { constructorSymbol = lookupSymbolForPropertyAccess(l.expression.expression, thisParentContainer); } } @@ -2669,11 +2669,11 @@ namespace ts { } } - function bindSpecialPropertyDeclaration(node: PropertyAccessExpression) { + function bindSpecialPropertyDeclaration(node: PropertyAccessExpression | LiteralLikeElementAccessExpression) { if (node.expression.kind === SyntaxKind.ThisKeyword) { bindThisPropertyAssignment(node); } - else if (isPropertyAccessEntityNameExpression(node) && node.parent.parent.kind === SyntaxKind.SourceFile) { + else if (isBindableStaticAccessExpression(node) && node.parent.parent.kind === SyntaxKind.SourceFile) { if (isPrototypeAccess(node.expression)) { bindPrototypePropertyAssignment(node, node.parent); } @@ -2684,11 +2684,10 @@ namespace ts { } /** For `x.prototype = { p, ... }`, declare members p,... if `x` is function/class/{}, or not declared. */ - function bindPrototypeAssignment(node: BinaryExpression) { + function bindPrototypeAssignment(node: BindableStaticPropertyAssignmentExpression) { node.left.parent = node; node.right.parent = node; - const lhs = node.left as PropertyAccessEntityNameExpression; - bindPropertyAssignment(lhs.expression, lhs, /*isPrototypeProperty*/ false, /*containerIsClass*/ true); + bindPropertyAssignment(node.left.expression, node.left, /*isPrototypeProperty*/ false, /*containerIsClass*/ true); } function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) { @@ -2700,10 +2699,10 @@ namespace ts { * For `x.prototype.y = z`, declare a member `y` on `x` if `x` is a function or class, or not declared. * Note that jsdoc preceding an ExpressionStatement like `x.prototype.y;` is also treated as a declaration. */ - function bindPrototypePropertyAssignment(lhs: PropertyAccessEntityNameExpression, parent: Node) { + function bindPrototypePropertyAssignment(lhs: BindableStaticAccessExpression, parent: Node) { // Look up the function in the local scope, since prototype assignments should // follow the function declaration - const classPrototype = lhs.expression as PropertyAccessEntityNameExpression; + const classPrototype = lhs.expression as BindableStaticAccessExpression; const constructorFunction = classPrototype.expression; // Fix up parent pointers since we're going to use these nodes before we bind into them @@ -2721,30 +2720,29 @@ namespace ts { bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false); } - function bindSpecialPropertyAssignment(node: BinaryExpression) { - const lhs = node.left as PropertyAccessEntityNameExpression; + function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) { // Class declarations in Typescript do not allow property declarations - const parentSymbol = lookupSymbolForPropertyAccess(lhs.expression); + const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression); if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) { return; } // Fix up parent pointers since we're going to use these nodes before we bind into them node.left.parent = node; node.right.parent = node; - if (isIdentifier(lhs.expression) && container === file && isExportsOrModuleExportsOrAlias(file, lhs.expression)) { + if (isIdentifier(node.left.expression) && container === file && isExportsOrModuleExportsOrAlias(file, node.left.expression)) { // This can be an alias for the 'exports' or 'module.exports' names, e.g. // var util = module.exports; // util.property = function ... - bindExportsPropertyAssignment(node); + bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression); } else { if (hasDynamicName(node)) { bindAnonymousDeclaration(node, SymbolFlags.Property | SymbolFlags.Assignment, InternalSymbolName.Computed); - const sym = bindPotentiallyMissingNamespaces(parentSymbol, lhs.expression, isTopLevelNamespaceAssignment(lhs), /*isPrototype*/ false, /*containerIsClass*/ false); + const sym = bindPotentiallyMissingNamespaces(parentSymbol, node.left.expression, isTopLevelNamespaceAssignment(node.left), /*isPrototype*/ false, /*containerIsClass*/ false); addLateBoundAssignmentDeclarationToSymbol(node, sym); } else { - bindStaticPropertyAssignment(lhs); + bindStaticPropertyAssignment(cast(node.left, isBindableStaticAccessExpression)); } } } @@ -2753,12 +2751,12 @@ namespace ts { * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function (or IIFE) or class or {}, or not declared. * Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y; */ - function bindStaticPropertyAssignment(node: PropertyAccessEntityNameExpression) { + function bindStaticPropertyAssignment(node: BindableStaticAccessExpression) { node.expression.parent = node; bindPropertyAssignment(node.expression, node, /*isPrototypeProperty*/ false, /*containerIsClass*/ false); } - function bindPotentiallyMissingNamespaces(namespaceSymbol: Symbol | undefined, entityName: EntityNameExpression, isToplevel: boolean, isPrototypeProperty: boolean, containerIsClass: boolean) { + function bindPotentiallyMissingNamespaces(namespaceSymbol: Symbol | undefined, entityName: BindableStaticNameExpression, isToplevel: boolean, isPrototypeProperty: boolean, containerIsClass: boolean) { if (isToplevel && !isPrototypeProperty) { // make symbols or add declarations for intermediate containers const flags = SymbolFlags.Module | SymbolFlags.Assignment; @@ -2781,7 +2779,7 @@ namespace ts { return namespaceSymbol; } - function bindPotentiallyNewExpandoMemberToNamespace(declaration: PropertyAccessEntityNameExpression | CallExpression, namespaceSymbol: Symbol | undefined, isPrototypeProperty: boolean) { + function bindPotentiallyNewExpandoMemberToNamespace(declaration: BindableStaticAccessExpression | CallExpression, namespaceSymbol: Symbol | undefined, isPrototypeProperty: boolean) { if (!namespaceSymbol || !isExpandoSymbol(namespaceSymbol)) { return; } @@ -2826,13 +2824,13 @@ namespace ts { declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment); } - function isTopLevelNamespaceAssignment(propertyAccess: PropertyAccessEntityNameExpression) { + function isTopLevelNamespaceAssignment(propertyAccess: BindableAccessExpression) { return isBinaryExpression(propertyAccess.parent) ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile; } - function bindPropertyAssignment(name: EntityNameExpression, propertyAccess: PropertyAccessEntityNameExpression, isPrototypeProperty: boolean, containerIsClass: boolean) { + function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) { let namespaceSymbol = lookupSymbolForPropertyAccess(name); const isToplevel = isTopLevelNamespaceAssignment(propertyAccess); namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty, containerIsClass); @@ -2877,17 +2875,17 @@ namespace ts { return expr.parent; } - function lookupSymbolForPropertyAccess(node: EntityNameExpression, lookupContainer: Node = container): Symbol | undefined { + function lookupSymbolForPropertyAccess(node: BindableStaticNameExpression, lookupContainer: Node = container): Symbol | undefined { if (isIdentifier(node)) { return lookupSymbolForNameWorker(lookupContainer, node.escapedText); } else { const symbol = lookupSymbolForPropertyAccess(node.expression); - return symbol && symbol.exports && symbol.exports.get(node.name.escapedText); + return symbol && symbol.exports && symbol.exports.get(escapeLeadingUnderscores(getElementOrPropertyAccessName(node))); } } - function forEachIdentifierInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined { + function forEachIdentifierInEntityName(e: BindableStaticNameExpression, parent: Symbol | undefined, action: (e: Declaration, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined { if (isExportsOrModuleExportsOrAlias(file, e)) { return file.symbol; } @@ -2896,7 +2894,7 @@ namespace ts { } else { const s = forEachIdentifierInEntityName(e.expression, parent, action); - return action(e.name, s && s.exports && s.exports.get(e.name.escapedText), s); + return action(getNameOrArgument(e), s && s.exports && s.exports.get(escapeLeadingUnderscores(getElementOrPropertyAccessName(e))), s); } } @@ -3170,7 +3168,7 @@ namespace ts { while (q.length && i < 100) { i++; node = q.shift()!; - if (isExportsIdentifier(node) || isModuleExportsPropertyAccessExpression(node)) { + if (isExportsIdentifier(node) || isModuleExportsAccessExpression(node)) { return true; } else if (isIdentifier(node)) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 97e08e7bccfe4..7b62484dfbd74 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3033,7 +3033,7 @@ namespace ts { return getSymbolOfNode(d.parent); } if (isClassExpression(d) && isBinaryExpression(d.parent) && d.parent.operatorToken.kind === SyntaxKind.EqualsToken && isAccessExpression(d.parent.left) && isEntityNameExpression(d.parent.left.expression)) { - if (isModuleExportsPropertyAccessExpression(d.parent.left) || isExportsIdentifier(d.parent.left.expression)) { + if (isModuleExportsAccessExpression(d.parent.left) || isExportsIdentifier(d.parent.left.expression)) { return getSymbolOfNode(getSourceFileOfNode(d)); } checkExpressionCached(d.parent.left.expression); @@ -4869,6 +4869,15 @@ namespace ts { } } + function getPropertyNameNodeForSymbol(symbol: Symbol, context: NodeBuilderContext) { + const fromNameType = getPropertyNameNodeForSymbolFromNameType(symbol, context); + if (fromNameType) { + return fromNameType; + } + const rawName = unescapeLeadingUnderscores(symbol.escapedName); + return createPropertyNameNodeForIdentifierOrLiteral(rawName); + } + // See getNameForSymbolFromNameType for a stringy equivalent function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext) { const nameType = symbol.nameType; @@ -4881,7 +4890,7 @@ namespace ts { if (isNumericLiteralName(name) && startsWith(name, "-")) { return createComputedPropertyName(createLiteral(+name)); } - return isIdentifierText(name, compilerOptions.target) ? createIdentifier(name) : createLiteral(isNumericLiteralName(name) ? +name : name) as StringLiteral | NumericLiteral; + return createPropertyNameNodeForIdentifierOrLiteral(name); } if (nameType.flags & TypeFlags.UniqueESSymbol) { return createComputedPropertyName(symbolToExpression((nameType).symbol, context, SymbolFlags.Value)); @@ -4889,6 +4898,10 @@ namespace ts { } } + function createPropertyNameNodeForIdentifierOrLiteral(name: string) { + return isIdentifierText(name, compilerOptions.target) ? createIdentifier(name) : createLiteral(isNumericLiteralName(name) ? +name : name) as StringLiteral | NumericLiteral; + } + function cloneNodeBuilderContext(context: NodeBuilderContext): NodeBuilderContext { const initial: NodeBuilderContext = { ...context }; // Make type parameters created within this context not consume the name outside this context @@ -5764,8 +5777,7 @@ namespace ts { return []; } const staticFlag = isStatic ? ModifierFlags.Static : 0; - const rawName = unescapeLeadingUnderscores(p.escapedName); - const name = getPropertyNameNodeForSymbolFromNameType(p, context) || createIdentifier(rawName); + const name = getPropertyNameNodeForSymbol(p, context); const firstPropertyLikeDecl = find(p.declarations, or(isPropertyDeclaration, isAccessor, isVariableDeclaration, isPropertySignature, isBinaryExpression, isPropertyAccessExpression)); if (p.flags & SymbolFlags.Accessor && useAccessors) { const result: AccessorDeclaration[] = []; @@ -6861,13 +6873,15 @@ namespace ts { let types: Type[] | undefined; for (const declaration of symbol.declarations) { const expression = (isBinaryExpression(declaration) || isCallExpression(declaration)) ? declaration : - isPropertyAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration : + isAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration : undefined; if (!expression) { continue; // Non-assignment declaration merged in (eg, an Identifier to mark the thing as a namespace) - skip over it and pull type info from elsewhere } - const kind = isPropertyAccessExpression(expression) ? getAssignmentDeclarationPropertyAccessKind(expression) : getAssignmentDeclarationKind(expression); + const kind = isAccessExpression(expression) + ? getAssignmentDeclarationPropertyAccessKind(expression) + : getAssignmentDeclarationKind(expression); if (kind === AssignmentDeclarationKind.ThisProperty) { if (isDeclarationInConstructor(expression)) { definedInConstructor = true; @@ -7242,12 +7256,15 @@ namespace ts { else if ( isBinaryExpression(declaration) || (isInJSFile(declaration) && - (isCallExpression(declaration) || isPropertyAccessExpression(declaration) && isBinaryExpression(declaration.parent)))) { + (isCallExpression(declaration) || (isPropertyAccessExpression(declaration) || isBindableStaticElementAccessExpression(declaration)) && isBinaryExpression(declaration.parent)))) { type = getWidenedTypeForAssignmentDeclaration(symbol); } else if (isJSDocPropertyLikeTag(declaration) || isPropertyAccessExpression(declaration) + || isElementAccessExpression(declaration) || isIdentifier(declaration) + || isStringLiteralLike(declaration) + || isNumericLiteral(declaration) || isClassDeclaration(declaration) || isFunctionDeclaration(declaration) || (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration)) @@ -7428,7 +7445,8 @@ namespace ts { return anyType; } else if (declaration.kind === SyntaxKind.BinaryExpression || - declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) { + (declaration.kind === SyntaxKind.PropertyAccessExpression || declaration.kind === SyntaxKind.ElementAccessExpression) && + declaration.parent.kind === SyntaxKind.BinaryExpression) { return getWidenedTypeForAssignmentDeclaration(symbol); } else if (symbol.flags & SymbolFlags.ValueModule && declaration && isSourceFile(declaration) && declaration.commonJsModuleIndicator) { @@ -32028,7 +32046,7 @@ namespace ts { return node; case SyntaxKind.PropertyAccessExpression: do { - if (isModuleExportsPropertyAccessExpression(node.expression)) { + if (isModuleExportsAccessExpression(node.expression)) { return node.name; } node = node.expression; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 36a70ec7d2562..e9b07af5dc00d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2260,7 +2260,7 @@ namespace ts { return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!) && (!dotHasTrivia || printerOptions.removeComments); } - else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) { + else if (isAccessExpression(expression)) { // check if constant enum value is integer const constantValue = getConstantValue(expression); // isFinite handles cases when constantValue is undefined diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4ac846fd5e115..919a0a873f812 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1301,7 +1301,7 @@ namespace ts { literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression; } - export interface StringLiteral extends LiteralExpression { + export interface StringLiteral extends LiteralExpression, Declaration { kind: SyntaxKind.StringLiteral; /* @internal */ textSourceNode?: Identifier | StringLiteralLike | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms). /** Note: this is only set when synthesizing a node, not during parsing. */ @@ -1688,7 +1688,7 @@ namespace ts { kind: SyntaxKind.RegularExpressionLiteral; } - export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode { + export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode, Declaration { kind: SyntaxKind.NoSubstitutionTemplateLiteral; } @@ -1717,7 +1717,7 @@ namespace ts { NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinaryOrOctalSpecifier | ContainsSeparator } - export interface NumericLiteral extends LiteralExpression { + export interface NumericLiteral extends LiteralExpression, Declaration { kind: SyntaxKind.NumericLiteral; /* @internal */ numericLiteralFlags: TokenFlags; @@ -1837,7 +1837,33 @@ namespace ts { } /** @internal */ - export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: EntityNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } }; + export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: BindableStaticNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } }; + /** @internal */ + export type BindableStaticNameExpression = EntityNameExpression | BindableStaticElementAccessExpression; + /** @internal */ + export type LiteralLikeElementAccessExpression = ElementAccessExpression & Declaration & { + argumentExpression: StringLiteralLike | NumericLiteral; + }; + /** @internal */ + export type BindableStaticElementAccessExpression = LiteralLikeElementAccessExpression & { + expression: BindableStaticNameExpression; + }; + /** @internal */ + export type BindableElementAccessExpression = ElementAccessExpression & { + expression: BindableStaticNameExpression; + }; + /** @internal */ + export type BindableStaticAccessExpression = PropertyAccessEntityNameExpression | BindableStaticElementAccessExpression; + /** @internal */ + export type BindableAccessExpression = PropertyAccessEntityNameExpression | BindableElementAccessExpression; + /** @internal */ + export interface BindableStaticPropertyAssignmentExpression extends BinaryExpression { + left: BindableStaticAccessExpression; + } + /** @internal */ + export interface BindablePropertyAssignmentExpression extends BinaryExpression { + left: BindableAccessExpression; + } // see: https://tc39.github.io/ecma262/#prod-SuperCall export interface SuperCall extends CallExpression { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1c7143f3705f7..7a41942c076be 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1874,7 +1874,7 @@ namespace ts { decl = name; } - if (!name || !isEntityNameExpression(name) || !isSameEntityName(name, node.parent.left)) { + if (!name || !isBindableStaticNameExpression(name) || !isSameEntityName(name, node.parent.left)) { return undefined; } } @@ -1886,7 +1886,7 @@ namespace ts { } export function isAssignmentDeclaration(decl: Declaration) { - return isBinaryExpression(decl) || isPropertyAccessExpression(decl) || isIdentifier(decl) || isCallExpression(decl); + return isBinaryExpression(decl) || isAccessExpression(decl) || isIdentifier(decl) || isCallExpression(decl); } /** Get the initializer, taking into account defaulted Javascript initializers */ @@ -1906,18 +1906,23 @@ namespace ts { } function hasExpandoValueProperty(node: ObjectLiteralExpression, isPrototypeAssignment: boolean) { - return forEach(node.properties, p => isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === "value" && p.initializer && getExpandoInitializer(p.initializer, isPrototypeAssignment)); + return forEach(node.properties, p => + isPropertyAssignment(p) && + isIdentifier(p.name) && + p.name.escapedText === "value" && + p.initializer && + getExpandoInitializer(p.initializer, isPrototypeAssignment)); } /** * Get the assignment 'initializer' -- the righthand side-- when the initializer is container-like (See getExpandoInitializer). * We treat the right hand side of assignments with container-like initalizers as declarations. */ - export function getAssignedExpandoInitializer(node: Node | undefined) { + export function getAssignedExpandoInitializer(node: Node | undefined): Expression | undefined { if (node && node.parent && isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken) { const isPrototypeAssignment = isPrototypeAccess(node.parent.left); return getExpandoInitializer(node.parent.right, isPrototypeAssignment) || - getDefaultedExpandoInitializer(node.parent.left as EntityNameExpression, node.parent.right, isPrototypeAssignment); + getDefaultedExpandoInitializer(node.parent.left, node.parent.right, isPrototypeAssignment); } if (node && isCallExpression(node) && isBindableObjectDefinePropertyCall(node)) { const result = hasExpandoValueProperty(node.arguments[2], node.arguments[1].text === "prototype"); @@ -1960,9 +1965,9 @@ namespace ts { * The second Lhs is required to be the same as the first except that it may be prefixed with * 'window.', 'global.' or 'self.' The second Lhs is otherwise ignored by the binder and checker. */ - function getDefaultedExpandoInitializer(name: EntityNameExpression, initializer: Expression, isPrototypeAssignment: boolean) { + function getDefaultedExpandoInitializer(name: Expression, initializer: Expression, isPrototypeAssignment: boolean) { const e = isBinaryExpression(initializer) && initializer.operatorToken.kind === SyntaxKind.BarBarToken && getExpandoInitializer(initializer.right, isPrototypeAssignment); - if (e && isSameEntityName(name, (initializer as BinaryExpression).left as EntityNameExpression)) { + if (e && isSameEntityName(name, (initializer as BinaryExpression).left)) { return e; } } @@ -1997,19 +2002,20 @@ namespace ts { * my.app = self.my.app || class { } */ function isSameEntityName(name: Expression, initializer: Expression): boolean { - if (isIdentifier(name) && isIdentifier(initializer)) { - return name.escapedText === initializer.escapedText; + if (isPropertyNameLiteral(name) && isPropertyNameLiteral(initializer)) { + return getTextOfIdentifierOrLiteral(name) === getTextOfIdentifierOrLiteral(name); } - if (isIdentifier(name) && isPropertyAccessExpression(initializer)) { - return (initializer.expression.kind as SyntaxKind.ThisKeyword === SyntaxKind.ThisKeyword || + if (isIdentifier(name) && (isLiteralLikeAccess(initializer))) { + return (initializer.expression.kind === SyntaxKind.ThisKeyword || isIdentifier(initializer.expression) && - (initializer.expression.escapedText === "window" as __String || - initializer.expression.escapedText === "self" as __String || - initializer.expression.escapedText === "global" as __String)) && - isSameEntityName(name, initializer.name); + (initializer.expression.escapedText === "window" || + initializer.expression.escapedText === "self" || + initializer.expression.escapedText === "global")) && + isSameEntityName(name, getNameOrArgument(initializer)); } - if (isPropertyAccessExpression(name) && isPropertyAccessExpression(initializer)) { - return name.name.escapedText === initializer.name.escapedText && isSameEntityName(name.expression, initializer.expression); + if (isLiteralLikeAccess(name) && isLiteralLikeAccess(initializer)) { + return getElementOrPropertyAccessName(name) === getElementOrPropertyAccessName(initializer) + && isSameEntityName(name.expression, initializer.expression); } return false; } @@ -2025,8 +2031,11 @@ namespace ts { return isIdentifier(node) && node.escapedText === "exports"; } - export function isModuleExportsPropertyAccessExpression(node: Node) { - return isPropertyAccessExpression(node) && isIdentifier(node.expression) && node.expression.escapedText === "module" && node.name.escapedText === "exports"; + export function isModuleExportsAccessExpression(node: Node): node is LiteralLikeElementAccessExpression & { expression: Identifier } { + return (isPropertyAccessExpression(node) || isLiteralLikeElementAccess(node)) + && isIdentifier(node.expression) + && node.expression.escapedText === "module" + && getElementOrPropertyAccessName(node) === "exports"; } /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property @@ -2043,7 +2052,38 @@ namespace ts { idText(expr.expression.expression) === "Object" && idText(expr.expression.name) === "defineProperty" && isStringOrNumericLiteralLike(expr.arguments[1]) && - isEntityNameExpression(expr.arguments[0]); + isBindableStaticNameExpression(expr.arguments[0]); + } + + export function isBindableStaticElementAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticElementAccessExpression { + return isLiteralLikeElementAccess(node) + && ((!excludeThisKeyword && node.expression.kind === SyntaxKind.ThisKeyword) || + isEntityNameExpression(node.expression) || + isBindableStaticElementAccessExpression(node.expression, /*excludeThisKeyword*/ true)); + } + + export function isLiteralLikeAccess(node: Node): node is LiteralLikeElementAccessExpression | PropertyAccessExpression { + return isPropertyAccessExpression(node) || isLiteralLikeElementAccess(node); + } + + export function isLiteralLikeElementAccess(node: Node): node is LiteralLikeElementAccessExpression { + return isElementAccessExpression(node) && isStringOrNumericLiteralLike(node.argumentExpression); + } + + export function isBindableStaticAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticAccessExpression { + return isPropertyAccessExpression(node) && (!excludeThisKeyword && node.expression.kind === SyntaxKind.ThisKeyword || isBindableStaticNameExpression(node.expression, /*excludeThisKeyword*/ true)) + || isBindableStaticElementAccessExpression(node, excludeThisKeyword); + } + + export function isBindableStaticNameExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticNameExpression { + return isEntityNameExpression(node) || isBindableStaticAccessExpression(node, excludeThisKeyword); + } + + export function getNameOrArgument(expr: PropertyAccessExpression | LiteralLikeElementAccessExpression) { + if (isPropertyAccessExpression(expr)) { + return expr.name; + } + return expr.argumentExpression; } function getAssignmentDeclarationKindWorker(expr: BinaryExpression | CallExpression): AssignmentDeclarationKind { @@ -2052,26 +2092,22 @@ namespace ts { return AssignmentDeclarationKind.None; } const entityName = expr.arguments[0]; - if (isExportsIdentifier(entityName) || isModuleExportsPropertyAccessExpression(entityName)) { + if (isExportsIdentifier(entityName) || isModuleExportsAccessExpression(entityName)) { return AssignmentDeclarationKind.ObjectDefinePropertyExports; } - if (isPropertyAccessExpression(entityName) && entityName.name.escapedText === "prototype" && isEntityNameExpression(entityName.expression)) { + if (isBindableStaticAccessExpression(entityName) && getElementOrPropertyAccessName(entityName) === "prototype") { return AssignmentDeclarationKind.ObjectDefinePrototypeProperty; } return AssignmentDeclarationKind.ObjectDefinePropertyValue; } - if (expr.operatorToken.kind !== SyntaxKind.EqualsToken) { + if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || !isAccessExpression(expr.left)) { return AssignmentDeclarationKind.None; } - const lhs = expr.left; - if (isAccessExpression(lhs)) { - if (isEntityNameExpression(lhs.expression) && getElementOrPropertyAccessName(lhs) === "prototype" && isObjectLiteralExpression(getInitializerOfBinaryExpression(expr))) { - // F.prototype = { ... } - return AssignmentDeclarationKind.Prototype; - } - return getAssignmentDeclarationPropertyAccessKind(lhs); + if (isBindableStaticNameExpression(expr.left.expression) && getElementOrPropertyAccessName(expr.left) === "prototype" && isObjectLiteralExpression(getInitializerOfBinaryExpression(expr))) { + // F.prototype = { ... } + return AssignmentDeclarationKind.Prototype; } - return AssignmentDeclarationKind.None; + return getAssignmentDeclarationPropertyAccessKind(expr.left); } /** @@ -2091,6 +2127,8 @@ namespace ts { } /* @internal */ + export function getElementOrPropertyAccessName(node: LiteralLikeElementAccessExpression | PropertyAccessExpression): string; + export function getElementOrPropertyAccessName(node: AccessExpression): string | undefined; export function getElementOrPropertyAccessName(node: AccessExpression): string | undefined { const name = getElementOrPropertyAccessArgumentExpressionOrName(node); if (name) { @@ -2108,22 +2146,21 @@ namespace ts { if (lhs.expression.kind === SyntaxKind.ThisKeyword) { return AssignmentDeclarationKind.ThisProperty; } - else if (isModuleExportsPropertyAccessExpression(lhs)) { + else if (isModuleExportsAccessExpression(lhs)) { // module.exports = expr return AssignmentDeclarationKind.ModuleExports; } - else if (isEntityNameExpression(lhs.expression)) { + else if (isBindableStaticNameExpression(lhs.expression, /*excludeThisKeyword*/ true)) { if (isPrototypeAccess(lhs.expression)) { // F.G....prototype.x = expr return AssignmentDeclarationKind.PrototypeProperty; } let nextToLast = lhs; - while (isAccessExpression(nextToLast.expression)) { - nextToLast = nextToLast.expression; + while (!isIdentifier(nextToLast.expression)) { + nextToLast = nextToLast.expression as Exclude; } - Debug.assert(isIdentifier(nextToLast.expression)); - const id = nextToLast.expression as Identifier; + const id = nextToLast.expression; if (id.escapedText === "exports" || id.escapedText === "module" && getElementOrPropertyAccessName(nextToLast) === "exports") { // exports.name = expr OR module.exports.name = expr @@ -2147,9 +2184,10 @@ namespace ts { return isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.PrototypeProperty; } - export function isSpecialPropertyDeclaration(expr: PropertyAccessExpression): boolean { + export function isSpecialPropertyDeclaration(expr: PropertyAccessExpression | ElementAccessExpression): expr is PropertyAccessExpression | LiteralLikeElementAccessExpression { return isInJSFile(expr) && expr.parent && expr.parent.kind === SyntaxKind.ExpressionStatement && + (!isElementAccessExpression(expr) || isLiteralLikeElementAccess(expr)) && !!getJSDocTypeTag(expr.parent); } @@ -4130,8 +4168,8 @@ namespace ts { return undefined; } - export function isPrototypeAccess(node: Node): node is PropertyAccessExpression { - return isPropertyAccessExpression(node) && node.name.escapedText === "prototype"; + export function isPrototypeAccess(node: Node): node is BindableStaticAccessExpression { + return isBindableStaticAccessExpression(node) && getElementOrPropertyAccessName(node) === "prototype"; } export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) { @@ -5357,6 +5395,11 @@ namespace ts { const { expression } = declaration as ExportAssignment; return isIdentifier(expression) ? expression : undefined; } + case SyntaxKind.ElementAccessExpression: + const expr = declaration as ElementAccessExpression; + if (isBindableStaticElementAccessExpression(expr)) { + return expr.argumentExpression; + } } return (declaration as NamedDeclaration).name; } @@ -5378,8 +5421,8 @@ namespace ts { if (isIdentifier(node.parent.left)) { return node.parent.left; } - else if (isPropertyAccessExpression(node.parent.left)) { - return node.parent.left.name; + else if (isAccessExpression(node.parent.left)) { + return getElementOrPropertyAccessArgumentExpressionOrName(node.parent.left); } } else if (isVariableDeclaration(node.parent) && isIdentifier(node.parent.name)) { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index b9875d30f530d..6de1de99cd759 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -135,12 +135,13 @@ namespace ts.NavigationBar { function endNestedNodes(depth: number): void { for (let i = 0; i < depth; i++) endNode(); } - function startNestedNodes(targetNode: Node, entityName: EntityNameExpression) { - const names: Identifier[] = []; - while (!isIdentifier(entityName)) { - const name = entityName.name; + function startNestedNodes(targetNode: Node, entityName: BindableStaticNameExpression) { + const names: PropertyNameLiteral[] = []; + while (!isPropertyNameLiteral(entityName)) { + const name = getNameOrArgument(entityName); + const nameText = getElementOrPropertyAccessName(entityName); entityName = entityName.expression; - if (name.escapedText === "prototype") continue; + if (nameText === "prototype") continue; names.push(name); } names.push(entityName); @@ -333,7 +334,7 @@ namespace ts.NavigationBar { assignmentTarget; let depth = 0; - let className: Identifier; + let className: PropertyNameLiteral; // If we see a prototype assignment, start tracking the target as a class // This is only done for simple classes not nested assignments. if (isIdentifier(prototypeAccess.expression)) { @@ -384,16 +385,16 @@ namespace ts.NavigationBar { } case AssignmentDeclarationKind.Property: { const binaryExpression = (node as BinaryExpression); - const assignmentTarget = binaryExpression.left as PropertyAccessExpression; + const assignmentTarget = binaryExpression.left as PropertyAccessExpression | BindableElementAccessExpression; const targetFunction = assignmentTarget.expression; - if (isIdentifier(targetFunction) && assignmentTarget.name.escapedText !== "prototype" && + if (isIdentifier(targetFunction) && getElementOrPropertyAccessName(assignmentTarget) !== "prototype" && trackedEs5Classes && trackedEs5Classes.has(targetFunction.text)) { if (isFunctionExpression(binaryExpression.right) || isArrowFunction(binaryExpression.right)) { addNodeWithRecursiveChild(node, binaryExpression.right, targetFunction); } - else { + else if (isBindableStaticAccessExpression(assignmentTarget)) { startNode(binaryExpression, targetFunction); - addNodeWithRecursiveChild(binaryExpression.left, binaryExpression.right, assignmentTarget.name); + addNodeWithRecursiveChild(binaryExpression.left, binaryExpression.right, getNameOrArgument(assignmentTarget)); endNode(); } return; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index c705080bc038a..13ad2e801601f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -847,7 +847,7 @@ declare namespace ts { kind: SyntaxKind.LiteralType; literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression; } - export interface StringLiteral extends LiteralExpression { + export interface StringLiteral extends LiteralExpression, Declaration { kind: SyntaxKind.StringLiteral; } export type StringLiteralLike = StringLiteral | NoSubstitutionTemplateLiteral; @@ -1011,7 +1011,7 @@ declare namespace ts { export interface RegularExpressionLiteral extends LiteralExpression { kind: SyntaxKind.RegularExpressionLiteral; } - export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode { + export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode, Declaration { kind: SyntaxKind.NoSubstitutionTemplateLiteral; } export enum TokenFlags { @@ -1022,7 +1022,7 @@ declare namespace ts { BinarySpecifier = 128, OctalSpecifier = 256, } - export interface NumericLiteral extends LiteralExpression { + export interface NumericLiteral extends LiteralExpression, Declaration { kind: SyntaxKind.NumericLiteral; } export interface BigIntLiteral extends LiteralExpression { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8ddf2033228a1..a5fc38bf9e239 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -847,7 +847,7 @@ declare namespace ts { kind: SyntaxKind.LiteralType; literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression; } - export interface StringLiteral extends LiteralExpression { + export interface StringLiteral extends LiteralExpression, Declaration { kind: SyntaxKind.StringLiteral; } export type StringLiteralLike = StringLiteral | NoSubstitutionTemplateLiteral; @@ -1011,7 +1011,7 @@ declare namespace ts { export interface RegularExpressionLiteral extends LiteralExpression { kind: SyntaxKind.RegularExpressionLiteral; } - export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode { + export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode, Declaration { kind: SyntaxKind.NoSubstitutionTemplateLiteral; } export enum TokenFlags { @@ -1022,7 +1022,7 @@ declare namespace ts { BinarySpecifier = 128, OctalSpecifier = 256, } - export interface NumericLiteral extends LiteralExpression { + export interface NumericLiteral extends LiteralExpression, Declaration { kind: SyntaxKind.NumericLiteral; } export interface BigIntLiteral extends LiteralExpression { diff --git a/tests/baselines/reference/moduleExportsElementAccessAssignment.js b/tests/baselines/reference/moduleExportsElementAccessAssignment.js new file mode 100644 index 0000000000000..daae7593d103d --- /dev/null +++ b/tests/baselines/reference/moduleExportsElementAccessAssignment.js @@ -0,0 +1,43 @@ +//// [tests/cases/conformance/jsdoc/moduleExportsElementAccessAssignment.ts] //// + +//// [mod1.js] +exports.a = { x: "x" }; +exports["b"] = { x: "x" }; +exports["default"] = { x: "x" }; +module.exports["c"] = { x: "x" }; +module["exports"]["d"] = {}; +module["exports"]["d"].e = 0; + +//// [mod2.js] +const mod1 = require("./mod1"); +mod1.a; +mod1.b; +mod1.c; +mod1.d; +mod1.d.e; +mod1.default; + + + +//// [mod1.d.ts] +export namespace a { + export const x: string; +} +export namespace b { + const x_1: string; + export { x_1 as x }; +} +declare namespace _default { + const x_2: string; + export { x_2 as x }; +} +export default _default; +export namespace c { + const x_3: string; + export { x_3 as x }; +} +export namespace d { + export const e: number; +} +//// [mod2.d.ts] +export {}; diff --git a/tests/baselines/reference/moduleExportsElementAccessAssignment.symbols b/tests/baselines/reference/moduleExportsElementAccessAssignment.symbols new file mode 100644 index 0000000000000..8032c898c934f --- /dev/null +++ b/tests/baselines/reference/moduleExportsElementAccessAssignment.symbols @@ -0,0 +1,74 @@ +=== tests/cases/conformance/jsdoc/mod2.js === +const mod1 = require("./mod1"); +>mod1 : Symbol(mod1, Decl(mod2.js, 0, 5)) +>require : Symbol(require) +>"./mod1" : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) + +mod1.a; +>mod1.a : Symbol(a, Decl(mod1.js, 0, 0)) +>mod1 : Symbol(mod1, Decl(mod2.js, 0, 5)) +>a : Symbol(a, Decl(mod1.js, 0, 0)) + +mod1.b; +>mod1.b : Symbol("b", Decl(mod1.js, 0, 23)) +>mod1 : Symbol(mod1, Decl(mod2.js, 0, 5)) +>b : Symbol("b", Decl(mod1.js, 0, 23)) + +mod1.c; +>mod1.c : Symbol("c", Decl(mod1.js, 2, 32)) +>mod1 : Symbol(mod1, Decl(mod2.js, 0, 5)) +>c : Symbol("c", Decl(mod1.js, 2, 32)) + +mod1.d; +>mod1.d : Symbol("d", Decl(mod1.js, 3, 33), Decl(mod1.js, 5, 18)) +>mod1 : Symbol(mod1, Decl(mod2.js, 0, 5)) +>d : Symbol("d", Decl(mod1.js, 3, 33), Decl(mod1.js, 5, 18)) + +mod1.d.e; +>mod1.d.e : Symbol("d".e, Decl(mod1.js, 4, 28)) +>mod1.d : Symbol("d", Decl(mod1.js, 3, 33), Decl(mod1.js, 5, 18)) +>mod1 : Symbol(mod1, Decl(mod2.js, 0, 5)) +>d : Symbol("d", Decl(mod1.js, 3, 33), Decl(mod1.js, 5, 18)) +>e : Symbol("d".e, Decl(mod1.js, 4, 28)) + +mod1.default; +>mod1.default : Symbol(default, Decl(mod1.js, 1, 26)) +>mod1 : Symbol(mod1, Decl(mod2.js, 0, 5)) +>default : Symbol(default, Decl(mod1.js, 1, 26)) + +=== tests/cases/conformance/jsdoc/mod1.js === +exports.a = { x: "x" }; +>exports.a : Symbol(a, Decl(mod1.js, 0, 0)) +>exports : Symbol(a, Decl(mod1.js, 0, 0)) +>a : Symbol(a, Decl(mod1.js, 0, 0)) +>x : Symbol(x, Decl(mod1.js, 0, 13)) + +exports["b"] = { x: "x" }; +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>"b" : Symbol("b", Decl(mod1.js, 0, 23)) +>x : Symbol(x, Decl(mod1.js, 1, 16)) + +exports["default"] = { x: "x" }; +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>"default" : Symbol("default", Decl(mod1.js, 1, 26)) +>x : Symbol(x, Decl(mod1.js, 2, 22)) + +module.exports["c"] = { x: "x" }; +>module.exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>module : Symbol(module, Decl(mod1.js, 2, 32)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>"c" : Symbol("c", Decl(mod1.js, 2, 32)) +>x : Symbol(x, Decl(mod1.js, 3, 23)) + +module["exports"]["d"] = {}; +>module : Symbol(module, Decl(mod1.js, 2, 32)) +>"exports" : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>"d" : Symbol("d", Decl(mod1.js, 3, 33), Decl(mod1.js, 5, 18)) + +module["exports"]["d"].e = 0; +>module["exports"]["d"].e : Symbol("d".e, Decl(mod1.js, 4, 28)) +>module : Symbol(module, Decl(mod1.js, 2, 32)) +>"exports" : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>"d" : Symbol("d", Decl(mod1.js, 3, 33), Decl(mod1.js, 5, 18)) +>e : Symbol("d".e, Decl(mod1.js, 4, 28)) + diff --git a/tests/baselines/reference/moduleExportsElementAccessAssignment.types b/tests/baselines/reference/moduleExportsElementAccessAssignment.types new file mode 100644 index 0000000000000..576bc227317e0 --- /dev/null +++ b/tests/baselines/reference/moduleExportsElementAccessAssignment.types @@ -0,0 +1,98 @@ +=== tests/cases/conformance/jsdoc/mod2.js === +const mod1 = require("./mod1"); +>mod1 : typeof import("tests/cases/conformance/jsdoc/mod1") +>require("./mod1") : typeof import("tests/cases/conformance/jsdoc/mod1") +>require : any +>"./mod1" : "./mod1" + +mod1.a; +>mod1.a : { x: string; } +>mod1 : typeof import("tests/cases/conformance/jsdoc/mod1") +>a : { x: string; } + +mod1.b; +>mod1.b : { x: string; } +>mod1 : typeof import("tests/cases/conformance/jsdoc/mod1") +>b : { x: string; } + +mod1.c; +>mod1.c : { x: string; } +>mod1 : typeof import("tests/cases/conformance/jsdoc/mod1") +>c : { x: string; } + +mod1.d; +>mod1.d : typeof import("tests/cases/conformance/jsdoc/mod1").d +>mod1 : typeof import("tests/cases/conformance/jsdoc/mod1") +>d : typeof import("tests/cases/conformance/jsdoc/mod1").d + +mod1.d.e; +>mod1.d.e : number +>mod1.d : typeof import("tests/cases/conformance/jsdoc/mod1").d +>mod1 : typeof import("tests/cases/conformance/jsdoc/mod1") +>d : typeof import("tests/cases/conformance/jsdoc/mod1").d +>e : number + +mod1.default; +>mod1.default : { x: string; } +>mod1 : typeof import("tests/cases/conformance/jsdoc/mod1") +>default : { x: string; } + +=== tests/cases/conformance/jsdoc/mod1.js === +exports.a = { x: "x" }; +>exports.a = { x: "x" } : { x: string; } +>exports.a : { x: string; } +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>a : { x: string; } +>{ x: "x" } : { x: string; } +>x : string +>"x" : "x" + +exports["b"] = { x: "x" }; +>exports["b"] = { x: "x" } : { x: string; } +>exports["b"] : { x: string; } +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"b" : "b" +>{ x: "x" } : { x: string; } +>x : string +>"x" : "x" + +exports["default"] = { x: "x" }; +>exports["default"] = { x: "x" } : { x: string; } +>exports["default"] : { x: string; } +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"default" : "default" +>{ x: "x" } : { x: string; } +>x : string +>"x" : "x" + +module.exports["c"] = { x: "x" }; +>module.exports["c"] = { x: "x" } : { x: string; } +>module.exports["c"] : { x: string; } +>module.exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>module : { "tests/cases/conformance/jsdoc/mod1": typeof import("tests/cases/conformance/jsdoc/mod1"); } +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"c" : "c" +>{ x: "x" } : { x: string; } +>x : string +>"x" : "x" + +module["exports"]["d"] = {}; +>module["exports"]["d"] = {} : typeof "d" +>module["exports"]["d"] : typeof "d" +>module["exports"] : typeof import("tests/cases/conformance/jsdoc/mod1") +>module : { "tests/cases/conformance/jsdoc/mod1": typeof import("tests/cases/conformance/jsdoc/mod1"); } +>"exports" : "exports" +>"d" : "d" +>{} : {} + +module["exports"]["d"].e = 0; +>module["exports"]["d"].e = 0 : 0 +>module["exports"]["d"].e : number +>module["exports"]["d"] : typeof "d" +>module["exports"] : typeof import("tests/cases/conformance/jsdoc/mod1") +>module : { "tests/cases/conformance/jsdoc/mod1": typeof import("tests/cases/conformance/jsdoc/mod1"); } +>"exports" : "exports" +>"d" : "d" +>e : number +>0 : 0 + diff --git a/tests/baselines/reference/thisPropertyAssignment.errors.txt b/tests/baselines/reference/thisPropertyAssignment.errors.txt new file mode 100644 index 0000000000000..77754631a799e --- /dev/null +++ b/tests/baselines/reference/thisPropertyAssignment.errors.txt @@ -0,0 +1,43 @@ +tests/cases/conformance/salsa/a.js(9,8): error TS2339: Property 'y' does not exist on type '{}'. +tests/cases/conformance/salsa/a.js(11,1): error TS7053: Element implicitly has an 'any' type because expression of type '"z"' can't be used to index type '{}'. + Property 'z' does not exist on type '{}'. +tests/cases/conformance/salsa/a.js(16,10): error TS2339: Property 'b' does not exist on type '{}'. +tests/cases/conformance/salsa/a.js(18,3): error TS7053: Element implicitly has an 'any' type because expression of type '"c"' can't be used to index type '{}'. + Property 'c' does not exist on type '{}'. + + +==== tests/cases/conformance/salsa/a.js (4 errors) ==== + // This test is asserting that a single property/element access + // on `this` is a special assignment declaration, but chaining + // off that does not create additional declarations. I’m not sure + // if it needs to be that way in JavaScript; the test simply + // ensures no accidental changes were introduced while allowing + // element access assignments to create declarations. + + this.x = {}; + this.x.y = {}; + ~ +!!! error TS2339: Property 'y' does not exist on type '{}'. + this["y"] = {}; + this["y"]["z"] = {}; + ~~~~~~~~~~~~~~ +!!! error TS7053: Element implicitly has an 'any' type because expression of type '"z"' can't be used to index type '{}'. +!!! error TS7053: Property 'z' does not exist on type '{}'. + + /** @constructor */ + function F() { + this.a = {}; + this.a.b = {}; + ~ +!!! error TS2339: Property 'b' does not exist on type '{}'. + this["b"] = {}; + this["b"]["c"] = {}; + ~~~~~~~~~~~~~~ +!!! error TS7053: Element implicitly has an 'any' type because expression of type '"c"' can't be used to index type '{}'. +!!! error TS7053: Property 'c' does not exist on type '{}'. + } + + const f = new F(); + f.a; + f.b; + \ No newline at end of file diff --git a/tests/baselines/reference/thisPropertyAssignment.js b/tests/baselines/reference/thisPropertyAssignment.js new file mode 100644 index 0000000000000..bd183fd622f81 --- /dev/null +++ b/tests/baselines/reference/thisPropertyAssignment.js @@ -0,0 +1,38 @@ +//// [a.js] +// This test is asserting that a single property/element access +// on `this` is a special assignment declaration, but chaining +// off that does not create additional declarations. I’m not sure +// if it needs to be that way in JavaScript; the test simply +// ensures no accidental changes were introduced while allowing +// element access assignments to create declarations. + +this.x = {}; +this.x.y = {}; +this["y"] = {}; +this["y"]["z"] = {}; + +/** @constructor */ +function F() { + this.a = {}; + this.a.b = {}; + this["b"] = {}; + this["b"]["c"] = {}; +} + +const f = new F(); +f.a; +f.b; + + + + +//// [a.d.ts] +/** @constructor */ +declare function F(): void; +declare class F { + a: {}; + b: {}; +} +declare var x: {} | undefined; +declare var y: {} | undefined; +declare const f: F; diff --git a/tests/baselines/reference/thisPropertyAssignment.symbols b/tests/baselines/reference/thisPropertyAssignment.symbols new file mode 100644 index 0000000000000..2517ae0f1425f --- /dev/null +++ b/tests/baselines/reference/thisPropertyAssignment.symbols @@ -0,0 +1,63 @@ +=== tests/cases/conformance/salsa/a.js === +// This test is asserting that a single property/element access +// on `this` is a special assignment declaration, but chaining +// off that does not create additional declarations. I’m not sure +// if it needs to be that way in JavaScript; the test simply +// ensures no accidental changes were introduced while allowing +// element access assignments to create declarations. + +this.x = {}; +>this.x : Symbol(x, Decl(a.js, 0, 0)) +>this : Symbol(globalThis) +>x : Symbol(x, Decl(a.js, 0, 0)) + +this.x.y = {}; +>this.x : Symbol(x, Decl(a.js, 0, 0)) +>this : Symbol(globalThis) +>x : Symbol(x, Decl(a.js, 0, 0)) + +this["y"] = {}; +>this : Symbol(globalThis) +>"y" : Symbol("y", Decl(a.js, 8, 14)) + +this["y"]["z"] = {}; +>this : Symbol(globalThis) +>"y" : Symbol("y", Decl(a.js, 8, 14)) + +/** @constructor */ +function F() { +>F : Symbol(F, Decl(a.js, 10, 20)) + + this.a = {}; +>this.a : Symbol(F.a, Decl(a.js, 13, 14)) +>this : Symbol(F, Decl(a.js, 10, 20)) +>a : Symbol(F.a, Decl(a.js, 13, 14)) + + this.a.b = {}; +>this.a : Symbol(F.a, Decl(a.js, 13, 14)) +>this : Symbol(F, Decl(a.js, 10, 20)) +>a : Symbol(F.a, Decl(a.js, 13, 14)) + + this["b"] = {}; +>this : Symbol(F, Decl(a.js, 10, 20)) +>"b" : Symbol(F["b"], Decl(a.js, 15, 16)) + + this["b"]["c"] = {}; +>this : Symbol(F, Decl(a.js, 10, 20)) +>"b" : Symbol(F["b"], Decl(a.js, 15, 16)) +} + +const f = new F(); +>f : Symbol(f, Decl(a.js, 20, 5)) +>F : Symbol(F, Decl(a.js, 10, 20)) + +f.a; +>f.a : Symbol(F.a, Decl(a.js, 13, 14)) +>f : Symbol(f, Decl(a.js, 20, 5)) +>a : Symbol(F.a, Decl(a.js, 13, 14)) + +f.b; +>f.b : Symbol(F["b"], Decl(a.js, 15, 16)) +>f : Symbol(f, Decl(a.js, 20, 5)) +>b : Symbol(F["b"], Decl(a.js, 15, 16)) + diff --git a/tests/baselines/reference/thisPropertyAssignment.types b/tests/baselines/reference/thisPropertyAssignment.types new file mode 100644 index 0000000000000..c919f47db1eec --- /dev/null +++ b/tests/baselines/reference/thisPropertyAssignment.types @@ -0,0 +1,92 @@ +=== tests/cases/conformance/salsa/a.js === +// This test is asserting that a single property/element access +// on `this` is a special assignment declaration, but chaining +// off that does not create additional declarations. I’m not sure +// if it needs to be that way in JavaScript; the test simply +// ensures no accidental changes were introduced while allowing +// element access assignments to create declarations. + +this.x = {}; +>this.x = {} : {} +>this.x : {} | undefined +>this : typeof globalThis +>x : {} | undefined +>{} : {} + +this.x.y = {}; +>this.x.y = {} : {} +>this.x.y : any +>this.x : {} +>this : typeof globalThis +>x : {} +>y : any +>{} : {} + +this["y"] = {}; +>this["y"] = {} : {} +>this["y"] : {} | undefined +>this : typeof globalThis +>"y" : "y" +>{} : {} + +this["y"]["z"] = {}; +>this["y"]["z"] = {} : {} +>this["y"]["z"] : any +>this["y"] : {} +>this : typeof globalThis +>"y" : "y" +>"z" : "z" +>{} : {} + +/** @constructor */ +function F() { +>F : typeof F + + this.a = {}; +>this.a = {} : {} +>this.a : {} +>this : this +>a : {} +>{} : {} + + this.a.b = {}; +>this.a.b = {} : {} +>this.a.b : any +>this.a : {} +>this : this +>a : {} +>b : any +>{} : {} + + this["b"] = {}; +>this["b"] = {} : {} +>this["b"] : {} +>this : this +>"b" : "b" +>{} : {} + + this["b"]["c"] = {}; +>this["b"]["c"] = {} : {} +>this["b"]["c"] : any +>this["b"] : {} +>this : this +>"b" : "b" +>"c" : "c" +>{} : {} +} + +const f = new F(); +>f : F +>new F() : F +>F : typeof F + +f.a; +>f.a : {} +>f : F +>a : {} + +f.b; +>f.b : {} +>f : F +>b : {} + diff --git a/tests/baselines/reference/typeFromPropertyAssignment38.symbols b/tests/baselines/reference/typeFromPropertyAssignment38.symbols new file mode 100644 index 0000000000000..657fcd97006e0 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment38.symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/salsa/typeFromPropertyAssignment38.ts === +function F() {} +>F : Symbol(F, Decl(typeFromPropertyAssignment38.ts, 0, 0), Decl(typeFromPropertyAssignment38.ts, 0, 15)) + +F["prop"] = 3; +>F : Symbol(F, Decl(typeFromPropertyAssignment38.ts, 0, 0), Decl(typeFromPropertyAssignment38.ts, 0, 15)) +>"prop" : Symbol(F["prop"], Decl(typeFromPropertyAssignment38.ts, 0, 15)) + +const f = function () {}; +>f : Symbol(f, Decl(typeFromPropertyAssignment38.ts, 3, 5), Decl(typeFromPropertyAssignment38.ts, 3, 25)) + +f["prop"] = 3; +>f : Symbol(f, Decl(typeFromPropertyAssignment38.ts, 3, 5), Decl(typeFromPropertyAssignment38.ts, 3, 25)) +>"prop" : Symbol(f["prop"], Decl(typeFromPropertyAssignment38.ts, 3, 25)) + diff --git a/tests/baselines/reference/typeFromPropertyAssignment38.types b/tests/baselines/reference/typeFromPropertyAssignment38.types new file mode 100644 index 0000000000000..ce392aa9762d8 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment38.types @@ -0,0 +1,22 @@ +=== tests/cases/conformance/salsa/typeFromPropertyAssignment38.ts === +function F() {} +>F : typeof F + +F["prop"] = 3; +>F["prop"] = 3 : 3 +>F["prop"] : number +>F : typeof F +>"prop" : "prop" +>3 : 3 + +const f = function () {}; +>f : { (): void; "prop": number; } +>function () {} : { (): void; "prop": number; } + +f["prop"] = 3; +>f["prop"] = 3 : 3 +>f["prop"] : number +>f : { (): void; "prop": number; } +>"prop" : "prop" +>3 : 3 + diff --git a/tests/baselines/reference/typeFromPropertyAssignment39.js b/tests/baselines/reference/typeFromPropertyAssignment39.js new file mode 100644 index 0000000000000..fb477aca063b9 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment39.js @@ -0,0 +1,14 @@ +//// [a.js] +const foo = {}; +foo["baz"] = {}; +foo["baz"]["blah"] = 3; + + + + +//// [a.d.ts] +declare namespace foo { + namespace baz { + const blah: number; + } +} diff --git a/tests/baselines/reference/typeFromPropertyAssignment39.symbols b/tests/baselines/reference/typeFromPropertyAssignment39.symbols new file mode 100644 index 0000000000000..622b77ed008dd --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment39.symbols @@ -0,0 +1,13 @@ +=== tests/cases/conformance/salsa/a.js === +const foo = {}; +>foo : Symbol(foo, Decl(a.js, 0, 5), Decl(a.js, 0, 15), Decl(a.js, 1, 16)) + +foo["baz"] = {}; +>foo : Symbol(foo, Decl(a.js, 0, 5), Decl(a.js, 0, 15), Decl(a.js, 1, 16)) +>"baz" : Symbol(foo["baz"], Decl(a.js, 0, 15), Decl(a.js, 2, 4)) + +foo["baz"]["blah"] = 3; +>foo : Symbol(foo, Decl(a.js, 0, 5), Decl(a.js, 0, 15), Decl(a.js, 1, 16)) +>"baz" : Symbol(foo["baz"], Decl(a.js, 0, 15), Decl(a.js, 2, 4)) +>"blah" : Symbol(foo["baz"]["blah"], Decl(a.js, 1, 16)) + diff --git a/tests/baselines/reference/typeFromPropertyAssignment39.types b/tests/baselines/reference/typeFromPropertyAssignment39.types new file mode 100644 index 0000000000000..ce2d028524261 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment39.types @@ -0,0 +1,21 @@ +=== tests/cases/conformance/salsa/a.js === +const foo = {}; +>foo : typeof foo +>{} : {} + +foo["baz"] = {}; +>foo["baz"] = {} : typeof foo.baz +>foo["baz"] : typeof foo.baz +>foo : typeof foo +>"baz" : "baz" +>{} : {} + +foo["baz"]["blah"] = 3; +>foo["baz"]["blah"] = 3 : 3 +>foo["baz"]["blah"] : number +>foo["baz"] : typeof foo.baz +>foo : typeof foo +>"baz" : "baz" +>"blah" : "blah" +>3 : 3 + diff --git a/tests/baselines/reference/typeFromPrototypeAssignment4.js b/tests/baselines/reference/typeFromPrototypeAssignment4.js new file mode 100644 index 0000000000000..0bea8aa81e33e --- /dev/null +++ b/tests/baselines/reference/typeFromPrototypeAssignment4.js @@ -0,0 +1,42 @@ +//// [a.js] +function Multimap4() { + this._map = {}; +}; + +Multimap4["prototype"] = { + /** + * @param {string} key + * @returns {number} the value ok + */ + get(key) { + return this._map[key + '']; + } +}; + +Multimap4["prototype"]["add-on"] = function() {}; +Multimap4["prototype"]["addon"] = function() {}; +Multimap4["prototype"]["__underscores__"] = function() {}; + +const map4 = new Multimap4(); +map4.get(""); +map4["add-on"](); +map4.addon(); +map4.__underscores__(); + + + + +//// [a.d.ts] +declare function Multimap4(): void; +declare class Multimap4 { + _map: {}; + "add-on"(): void; + addon(): void; + __underscores__(): void; + /** + * @param {string} key + * @returns {number} the value ok + */ + get(key: string): number; +} +declare const map4: Multimap4; diff --git a/tests/baselines/reference/typeFromPrototypeAssignment4.symbols b/tests/baselines/reference/typeFromPrototypeAssignment4.symbols new file mode 100644 index 0000000000000..bc7e816cc941c --- /dev/null +++ b/tests/baselines/reference/typeFromPrototypeAssignment4.symbols @@ -0,0 +1,64 @@ +=== tests/cases/conformance/salsa/a.js === +function Multimap4() { +>Multimap4 : Symbol(Multimap4, Decl(a.js, 0, 0), Decl(a.js, 2, 2)) + + this._map = {}; +>_map : Symbol(Multimap4._map, Decl(a.js, 0, 22)) + +}; + +Multimap4["prototype"] = { +>Multimap4 : Symbol(Multimap4, Decl(a.js, 0, 0), Decl(a.js, 2, 2)) +>"prototype" : Symbol(Multimap4["prototype"], Decl(a.js, 2, 2)) + + /** + * @param {string} key + * @returns {number} the value ok + */ + get(key) { +>get : Symbol(get, Decl(a.js, 4, 26)) +>key : Symbol(key, Decl(a.js, 9, 6)) + + return this._map[key + '']; +>this._map : Symbol(Multimap4._map, Decl(a.js, 0, 22)) +>this : Symbol(Multimap4, Decl(a.js, 0, 0), Decl(a.js, 2, 2)) +>_map : Symbol(Multimap4._map, Decl(a.js, 0, 22)) +>key : Symbol(key, Decl(a.js, 9, 6)) + } +}; + +Multimap4["prototype"]["add-on"] = function() {}; +>Multimap4 : Symbol(Multimap4, Decl(a.js, 0, 0), Decl(a.js, 2, 2)) +>"prototype" : Symbol(Multimap4["prototype"], Decl(a.js, 2, 2)) + +Multimap4["prototype"]["addon"] = function() {}; +>Multimap4 : Symbol(Multimap4, Decl(a.js, 0, 0), Decl(a.js, 2, 2)) +>"prototype" : Symbol(Multimap4["prototype"], Decl(a.js, 2, 2)) + +Multimap4["prototype"]["__underscores__"] = function() {}; +>Multimap4 : Symbol(Multimap4, Decl(a.js, 0, 0), Decl(a.js, 2, 2)) +>"prototype" : Symbol(Multimap4["prototype"], Decl(a.js, 2, 2)) + +const map4 = new Multimap4(); +>map4 : Symbol(map4, Decl(a.js, 18, 5)) +>Multimap4 : Symbol(Multimap4, Decl(a.js, 0, 0), Decl(a.js, 2, 2)) + +map4.get(""); +>map4.get : Symbol(get, Decl(a.js, 4, 26)) +>map4 : Symbol(map4, Decl(a.js, 18, 5)) +>get : Symbol(get, Decl(a.js, 4, 26)) + +map4["add-on"](); +>map4 : Symbol(map4, Decl(a.js, 18, 5)) +>"add-on" : Symbol(Multimap4["add-on"], Decl(a.js, 12, 2)) + +map4.addon(); +>map4.addon : Symbol(Multimap4["addon"], Decl(a.js, 14, 49)) +>map4 : Symbol(map4, Decl(a.js, 18, 5)) +>addon : Symbol(Multimap4["addon"], Decl(a.js, 14, 49)) + +map4.__underscores__(); +>map4.__underscores__ : Symbol(Multimap4["__underscores__"], Decl(a.js, 15, 48)) +>map4 : Symbol(map4, Decl(a.js, 18, 5)) +>__underscores__ : Symbol(Multimap4["__underscores__"], Decl(a.js, 15, 48)) + diff --git a/tests/baselines/reference/typeFromPrototypeAssignment4.types b/tests/baselines/reference/typeFromPrototypeAssignment4.types new file mode 100644 index 0000000000000..42f340ac7a956 --- /dev/null +++ b/tests/baselines/reference/typeFromPrototypeAssignment4.types @@ -0,0 +1,96 @@ +=== tests/cases/conformance/salsa/a.js === +function Multimap4() { +>Multimap4 : typeof Multimap4 + + this._map = {}; +>this._map = {} : {} +>this._map : any +>this : any +>_map : any +>{} : {} + +}; + +Multimap4["prototype"] = { +>Multimap4["prototype"] = { /** * @param {string} key * @returns {number} the value ok */ get(key) { return this._map[key + '']; }} : { get(key: string): number; } +>Multimap4["prototype"] : { get(key: string): number; } +>Multimap4 : typeof Multimap4 +>"prototype" : "prototype" +>{ /** * @param {string} key * @returns {number} the value ok */ get(key) { return this._map[key + '']; }} : { get(key: string): number; } + + /** + * @param {string} key + * @returns {number} the value ok + */ + get(key) { +>get : (key: string) => number +>key : string + + return this._map[key + '']; +>this._map[key + ''] : any +>this._map : {} +>this : this +>_map : {} +>key + '' : string +>key : string +>'' : "" + } +}; + +Multimap4["prototype"]["add-on"] = function() {}; +>Multimap4["prototype"]["add-on"] = function() {} : () => void +>Multimap4["prototype"]["add-on"] : any +>Multimap4["prototype"] : { get(key: string): number; } +>Multimap4 : typeof Multimap4 +>"prototype" : "prototype" +>"add-on" : "add-on" +>function() {} : () => void + +Multimap4["prototype"]["addon"] = function() {}; +>Multimap4["prototype"]["addon"] = function() {} : () => void +>Multimap4["prototype"]["addon"] : any +>Multimap4["prototype"] : { get(key: string): number; } +>Multimap4 : typeof Multimap4 +>"prototype" : "prototype" +>"addon" : "addon" +>function() {} : () => void + +Multimap4["prototype"]["__underscores__"] = function() {}; +>Multimap4["prototype"]["__underscores__"] = function() {} : () => void +>Multimap4["prototype"]["__underscores__"] : any +>Multimap4["prototype"] : { get(key: string): number; } +>Multimap4 : typeof Multimap4 +>"prototype" : "prototype" +>"__underscores__" : "__underscores__" +>function() {} : () => void + +const map4 = new Multimap4(); +>map4 : Multimap4 +>new Multimap4() : Multimap4 +>Multimap4 : typeof Multimap4 + +map4.get(""); +>map4.get("") : number +>map4.get : (key: string) => number +>map4 : Multimap4 +>get : (key: string) => number +>"" : "" + +map4["add-on"](); +>map4["add-on"]() : void +>map4["add-on"] : () => void +>map4 : Multimap4 +>"add-on" : "add-on" + +map4.addon(); +>map4.addon() : void +>map4.addon : () => void +>map4 : Multimap4 +>addon : () => void + +map4.__underscores__(); +>map4.__underscores__() : void +>map4.__underscores__ : () => void +>map4 : Multimap4 +>__underscores__ : () => void + diff --git a/tests/cases/conformance/jsdoc/moduleExportsElementAccessAssignment.ts b/tests/cases/conformance/jsdoc/moduleExportsElementAccessAssignment.ts new file mode 100644 index 0000000000000..ed28f107a4045 --- /dev/null +++ b/tests/cases/conformance/jsdoc/moduleExportsElementAccessAssignment.ts @@ -0,0 +1,21 @@ +// @allowJs: true +// @strict: true +// @checkJs: true +// @emitDeclarationOnly: true +// @declaration: true +// @filename: mod1.js +exports.a = { x: "x" }; +exports["b"] = { x: "x" }; +exports["default"] = { x: "x" }; +module.exports["c"] = { x: "x" }; +module["exports"]["d"] = {}; +module["exports"]["d"].e = 0; + +// @filename: mod2.js +const mod1 = require("./mod1"); +mod1.a; +mod1.b; +mod1.c; +mod1.d; +mod1.d.e; +mod1.default; \ No newline at end of file diff --git a/tests/cases/conformance/salsa/thisPropertyAssignment.ts b/tests/cases/conformance/salsa/thisPropertyAssignment.ts new file mode 100644 index 0000000000000..44716cb7016be --- /dev/null +++ b/tests/cases/conformance/salsa/thisPropertyAssignment.ts @@ -0,0 +1,30 @@ +// @checkJs: true +// @allowJs: true +// @strict: true +// @emitDeclarationOnly: true +// @declaration: true +// @Filename: a.js + +// This test is asserting that a single property/element access +// on `this` is a special assignment declaration, but chaining +// off that does not create additional declarations. I’m not sure +// if it needs to be that way in JavaScript; the test simply +// ensures no accidental changes were introduced while allowing +// element access assignments to create declarations. + +this.x = {}; +this.x.y = {}; +this["y"] = {}; +this["y"]["z"] = {}; + +/** @constructor */ +function F() { + this.a = {}; + this.a.b = {}; + this["b"] = {}; + this["b"]["c"] = {}; +} + +const f = new F(); +f.a; +f.b; diff --git a/tests/cases/conformance/salsa/typeFromPropertyAssignment38.ts b/tests/cases/conformance/salsa/typeFromPropertyAssignment38.ts new file mode 100644 index 0000000000000..86104cfb0b62e --- /dev/null +++ b/tests/cases/conformance/salsa/typeFromPropertyAssignment38.ts @@ -0,0 +1,9 @@ +// @noEmit: true +// @strict: true +// @declaration: true + +function F() {} +F["prop"] = 3; + +const f = function () {}; +f["prop"] = 3; diff --git a/tests/cases/conformance/salsa/typeFromPropertyAssignment39.ts b/tests/cases/conformance/salsa/typeFromPropertyAssignment39.ts new file mode 100644 index 0000000000000..17fbde3010e06 --- /dev/null +++ b/tests/cases/conformance/salsa/typeFromPropertyAssignment39.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @checkJs: true +// @strict: true +// @emitDeclarationOnly: true +// @declaration: true +// @Filename: a.js + +const foo = {}; +foo["baz"] = {}; +foo["baz"]["blah"] = 3; diff --git a/tests/cases/conformance/salsa/typeFromPrototypeAssignment4.ts b/tests/cases/conformance/salsa/typeFromPrototypeAssignment4.ts new file mode 100644 index 0000000000000..426e327981c6e --- /dev/null +++ b/tests/cases/conformance/salsa/typeFromPrototypeAssignment4.ts @@ -0,0 +1,30 @@ +// @allowJs: true +// @checkJs: true +// @emitDeclarationOnly: true +// @declaration: true +// @outDir: out +// @Filename: a.js + +function Multimap4() { + this._map = {}; +}; + +Multimap4["prototype"] = { + /** + * @param {string} key + * @returns {number} the value ok + */ + get(key) { + return this._map[key + '']; + } +}; + +Multimap4["prototype"]["add-on"] = function() {}; +Multimap4["prototype"]["addon"] = function() {}; +Multimap4["prototype"]["__underscores__"] = function() {}; + +const map4 = new Multimap4(); +map4.get(""); +map4["add-on"](); +map4.addon(); +map4.__underscores__(); diff --git a/tests/cases/fourslash/goToDefinitionExpandoElementAccess.ts b/tests/cases/fourslash/goToDefinitionExpandoElementAccess.ts new file mode 100644 index 0000000000000..5deeb4c02532e --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionExpandoElementAccess.ts @@ -0,0 +1,7 @@ +/// + +////function f() {} +////f[/*0*/"x"] = 0; +////f[[|/*1*/"x"|]] = 1; + +verify.goToDefinition("1", "0"); diff --git a/tests/cases/fourslash/navigationBarFunctionPrototype4.ts b/tests/cases/fourslash/navigationBarFunctionPrototype4.ts index c24261ee8230d..f12ec4ff7dba0 100644 --- a/tests/cases/fourslash/navigationBarFunctionPrototype4.ts +++ b/tests/cases/fourslash/navigationBarFunctionPrototype4.ts @@ -7,13 +7,19 @@ ////A.prototype = { m() {} }; ////A.prototype.a = function() { }; ////A.b = function() { }; +//// +////var B; +////B["prototype"] = { }; +////B["prototype"] = { m() {} }; +////B["prototype"]["a"] = function() { }; +////B["b"] = function() { }; verify.navigationTree({ "text": "", "kind": "script", - "childItems": [ + "childItems": [{ name: "A", quoted: false }, { name: "B", quoted: true }].map(({ name, quoted }) => ( { - "text": "A", + "text": name, "kind": "class", "childItems": [ { @@ -25,31 +31,31 @@ verify.navigationTree({ "kind": "method" }, { - "text": "a", + "text": quoted ? `"a"` : "a", "kind": "function" }, { - "text": "b", + "text": quoted ? `"b"` : "b", "kind": "function" } ] } - ] + )) }); verify.navigationBar([ { "text": "", "kind": "script", - "childItems": [ + "childItems": ["A", "B"].map(name => ( { - "text": "A", + "text": name, "kind": "class" } - ] + )) }, - { - "text": "A", + ...[{ name: "A", quoted: false }, { name: "B", quoted: true }].map(({ name, quoted }) => ({ + "text": name, "kind": "class", "childItems": [ { @@ -61,14 +67,14 @@ verify.navigationBar([ "kind": "method" }, { - "text": "a", + "text": quoted ? `"a"` : "a", "kind": "function" }, { - "text": "b", + "text": quoted ? `"b"` : "b", "kind": "function" } ], "indent": 1 - } + })) ]); diff --git a/tests/cases/fourslash/quickInfoElementAccessDeclaration.ts b/tests/cases/fourslash/quickInfoElementAccessDeclaration.ts new file mode 100644 index 0000000000000..4f36146887490 --- /dev/null +++ b/tests/cases/fourslash/quickInfoElementAccessDeclaration.ts @@ -0,0 +1,12 @@ +/// + +// @checkJs: true +// @allowJs: true +// @Filename: a.js +////const mod = {}; +////mod["@@thing1"] = {}; +////mod["/**/@@thing1"]["@@thing2"] = 0; + +goTo.marker(); +verify.quickInfoIs(`module mod["@@thing1"] +(property) mod["@@thing1"]: typeof mod.@@thing1`);