diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad3f3b71f29fb..07bba3dd0b238 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3057,9 +3057,20 @@ namespace ts { function resolveExternalModuleSymbol(moduleSymbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined; function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol { if (moduleSymbol) { + const links = getSymbolLinks(moduleSymbol); + if (!dontResolveAlias) { + links.resolvedExternalModuleSymbol = "circular"; + } const exportEquals = resolveSymbol(moduleSymbol.exports!.get(InternalSymbolName.ExportEquals), dontResolveAlias); const exported = getCommonJsExportEquals(getMergedSymbol(exportEquals), getMergedSymbol(moduleSymbol)); - return getMergedSymbol(exported) || moduleSymbol; + const result = getMergedSymbol(exported) || moduleSymbol; + if (!dontResolveAlias) { + // we "cache" this result, but because both the input _and_ the output are mergeable, literally every call could need + // a different result, assuming the inputs and outputs get merged with. So instead we just use this as a flag that + // export assignment resolution has occured on this symbol at least once. (because it might happen many times!) + links.resolvedExternalModuleSymbol = result; + } + return result; } return undefined!; } @@ -5531,7 +5542,7 @@ namespace ts { function serializeSymbolWorker(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean) { const symbolName = unescapeLeadingUnderscores(symbol.escapedName); const isDefault = symbol.escapedName === InternalSymbolName.Default; - if (isStringANonContextualKeyword(symbolName) && !isDefault) { + if ((isStringANonContextualKeyword(symbolName) || !isIdentifierText(symbolName, languageVersion)) && !isDefault && symbol.escapedName !== InternalSymbolName.ExportEquals) { // Oh no. We cannot use this symbol's name as it's name... It's likely some jsdoc had an invalid name like `export` or `default` :( context.encounteredError = true; // TODO: Issue error via symbol tracker? @@ -5682,7 +5693,8 @@ namespace ts { } function getNamespaceMembersForSerialization(symbol: Symbol) { - return !symbol.exports ? [] : filter(arrayFrom((symbol.exports).values()), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype"))); + const exports = getExportsOfSymbol(symbol); + return !exports ? [] : filter(arrayFrom((exports).values()), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype"))); } function isTypeOnlyNamespace(symbol: Symbol) { @@ -8884,6 +8896,17 @@ namespace ts { function getResolvedMembersOrExportsOfSymbol(symbol: Symbol, resolutionKind: MembersOrExportsResolutionKind): UnderscoreEscapedMap { const links = getSymbolLinks(symbol); if (!links[resolutionKind]) { + // We _must_ resolve any possible commonjs export assignments _before_ merging in late bound members, as cjs export assignments may cause + // things to be merged (?!?) into this symbol; moreover, we _can't_ resolve those export assignments if we're already resolving the containing + // module (as then we'll issue a circularity error) + const p = symbol.valueDeclaration?.parent; + if (p && isSourceFile(p) && p.symbol && !getSymbolLinks(p.symbol).resolvedExternalModuleSymbol) { + const exported = resolveExternalModuleSymbol(p.symbol); + const targetLinks = exported && getSymbolLinks(exported); + if (targetLinks && targetLinks[resolutionKind]) { + return links[resolutionKind] = targetLinks[resolutionKind]!; + } + } const isStatic = resolutionKind === MembersOrExportsResolutionKind.resolvedExports; const earlySymbols = !isStatic ? symbol.members : symbol.flags & SymbolFlags.Module ? getExportsOfModuleWorker(symbol) : @@ -35082,8 +35105,8 @@ namespace ts { } } - function createTypeOfDeclaration(declarationIn: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean) { - const declaration = getParseTreeNode(declarationIn, isVariableLikeOrAccessor); + function createTypeOfDeclaration(declarationIn: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean) { + const declaration = getParseTreeNode(declarationIn, isPossiblyPropertyLikeDeclaration); if (!declaration) { return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode; } diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 00417535145b0..605e441da67b5 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -1157,13 +1157,20 @@ namespace ts { fakespace.locals = createSymbolTable(props); fakespace.symbol = props[0].parent!; const declarations = mapDefined(props, p => { - if (!isPropertyAccessExpression(p.valueDeclaration)) { - return undefined; // TODO GH#33569: Handle element access expressions that created late bound names (rather than silently omitting them) + if (!isPropertyAccessExpression(p.valueDeclaration) && !isElementAccessExpression(p.valueDeclaration) && !isBinaryExpression(p.valueDeclaration)) { + return undefined; + } + if (hasDynamicName(p.valueDeclaration) && !resolver.isLateBound(getParseTreeNode(p.valueDeclaration) as Declaration)) { + return undefined; + } + const name = unescapeLeadingUnderscores(p.escapedName); + if (!isIdentifierText(name, ScriptTarget.ES3)) { + return undefined; // TODO: Rather than quietly eliding (as is current behavior), maybe we should issue errors? } getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(p.valueDeclaration); const type = resolver.createTypeOfDeclaration(p.valueDeclaration, fakespace, declarationEmitNodeBuilderFlags, symbolTracker); getSymbolAccessibilityDiagnostic = oldDiag; - const varDecl = createVariableDeclaration(unescapeLeadingUnderscores(p.escapedName), type, /*initializer*/ undefined); + const varDecl = createVariableDeclaration(name, type, /*initializer*/ undefined); return createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([varDecl])); }); const namespaceDecl = createModuleDeclaration(/*decorators*/ undefined, ensureModifiers(input), input.name!, createModuleBlock(declarations), NodeFlags.Namespace); diff --git a/src/compiler/transformers/declarations/diagnostics.ts b/src/compiler/transformers/declarations/diagnostics.ts index ac44f7efd15e5..1b7827f552f07 100644 --- a/src/compiler/transformers/declarations/diagnostics.ts +++ b/src/compiler/transformers/declarations/diagnostics.ts @@ -27,7 +27,9 @@ namespace ts { | TypeAliasDeclaration | ConstructorDeclaration | IndexSignatureDeclaration - | PropertyAccessExpression; + | PropertyAccessExpression + | ElementAccessExpression + | BinaryExpression; export function canProduceDiagnostics(node: Node): node is DeclarationDiagnosticProducing { return isVariableDeclaration(node) || @@ -48,7 +50,9 @@ namespace ts { isTypeAliasDeclaration(node) || isConstructorDeclaration(node) || isIndexSignatureDeclaration(node) || - isPropertyAccessExpression(node); + isPropertyAccessExpression(node) || + isElementAccessExpression(node) || + isBinaryExpression(node); } export function createGetSymbolAccessibilityDiagnosticForNodeName(node: DeclarationDiagnosticProducing) { @@ -125,7 +129,7 @@ namespace ts { } export function createGetSymbolAccessibilityDiagnosticForNode(node: DeclarationDiagnosticProducing): (symbolAccessibilityResult: SymbolAccessibilityResult) => SymbolAccessibilityDiagnostic | undefined { - if (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isPropertyAccessExpression(node) || isBindingElement(node) || isConstructorDeclaration(node)) { + if (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node) || isBinaryExpression(node) || isBindingElement(node) || isConstructorDeclaration(node)) { return getVariableDeclarationTypeVisibilityError; } else if (isSetAccessor(node) || isGetAccessor(node)) { @@ -166,7 +170,7 @@ namespace ts { } // This check is to ensure we don't report error on constructor parameter property as that error would be reported during parameter emit // The only exception here is if the constructor was marked as private. we are not emitting the constructor parameters at all. - else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.PropertySignature || + else if (node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression || node.kind === SyntaxKind.BinaryExpression || node.kind === SyntaxKind.PropertySignature || (node.kind === SyntaxKind.Parameter && hasModifier(node.parent, ModifierFlags.Private))) { // TODO(jfreeman): Deal with computed properties in error reporting. if (hasModifier(node, ModifierFlags.Static)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9b913c2f17d00..ba9affda43757 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3920,7 +3920,7 @@ namespace ts { isOptionalUninitializedParameterProperty(node: ParameterDeclaration): boolean; isExpandoFunctionDeclaration(node: FunctionDeclaration): boolean; getPropertiesOfContainerFunction(node: Declaration): Symbol[]; - createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined; + createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined; createReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined; createTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined; createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration, tracker: SymbolTracker): Expression; @@ -4094,6 +4094,7 @@ namespace ts { deferralParent?: Type; // Source union/intersection of a deferred type cjsExportMerged?: Symbol; // Version of the symbol with all non export= exports merged with the export= target typeOnlyDeclaration?: TypeOnlyCompatibleAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs + resolvedExternalModuleSymbol?: Symbol | "circular"; // Cached result of `resolveExternalModuleSymbol` } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bdee20ef861bf..09e4f35388348 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1296,6 +1296,10 @@ namespace ts { return isVariableLike(node) || isAccessor(node); } + export function isPossiblyPropertyLikeDeclaration(node: Node): node is AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression { + return isVariableLikeOrAccessor(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node) || isBinaryExpression(node); + } + export function isVariableDeclarationInVariableStatement(node: VariableDeclaration) { return node.parent.kind === SyntaxKind.VariableDeclarationList && node.parent.parent.kind === SyntaxKind.VariableStatement; diff --git a/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.js b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.js new file mode 100644 index 0000000000000..b32a31e9a3505 --- /dev/null +++ b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.js @@ -0,0 +1,40 @@ +//// [lateBoundElementAccessAssignmentDeclarations.ts] +export function foo() {} +foo.bar = 12; +const _private = Symbol(); +foo[_private] = "ok"; +const strMem = "strMemName"; +foo[strMem] = "ok"; +const dashStrMem = "dashed-str-mem"; +foo[dashStrMem] = "ok"; +const numMem = 42; +foo[numMem] = "ok"; + +const x: string = foo[_private]; +const y: string = foo[strMem]; +const z: string = foo[numMem]; +const a: string = foo[dashStrMem]; + +//// [lateBoundElementAccessAssignmentDeclarations.js] +export function foo() { } +foo.bar = 12; +const _private = Symbol(); +foo[_private] = "ok"; +const strMem = "strMemName"; +foo[strMem] = "ok"; +const dashStrMem = "dashed-str-mem"; +foo[dashStrMem] = "ok"; +const numMem = 42; +foo[numMem] = "ok"; +const x = foo[_private]; +const y = foo[strMem]; +const z = foo[numMem]; +const a = foo[dashStrMem]; + + +//// [lateBoundElementAccessAssignmentDeclarations.d.ts] +export declare function foo(): void; +export declare namespace foo { + var bar: number; + var strMemName: string; +} diff --git a/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.symbols b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.symbols new file mode 100644 index 0000000000000..d407959b0c312 --- /dev/null +++ b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.symbols @@ -0,0 +1,58 @@ +=== tests/cases/compiler/lateBoundElementAccessAssignmentDeclarations.ts === +export function foo() {} +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) + +foo.bar = 12; +>foo.bar : Symbol(foo.bar, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24)) +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>bar : Symbol(foo.bar, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24)) + +const _private = Symbol(); +>_private : Symbol(_private, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +foo[_private] = "ok"; +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>_private : Symbol(_private, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 5)) + +const strMem = "strMemName"; +>strMem : Symbol(strMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 5)) + +foo[strMem] = "ok"; +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>strMem : Symbol(strMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 5)) + +const dashStrMem = "dashed-str-mem"; +>dashStrMem : Symbol(dashStrMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 5)) + +foo[dashStrMem] = "ok"; +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>dashStrMem : Symbol(dashStrMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 5)) + +const numMem = 42; +>numMem : Symbol(numMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 8, 5)) + +foo[numMem] = "ok"; +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>numMem : Symbol(numMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 8, 5)) + +const x: string = foo[_private]; +>x : Symbol(x, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 11, 5)) +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>_private : Symbol(_private, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 5)) + +const y: string = foo[strMem]; +>y : Symbol(y, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 12, 5)) +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>strMem : Symbol(strMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 5)) + +const z: string = foo[numMem]; +>z : Symbol(z, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 13, 5)) +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>numMem : Symbol(numMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 8, 5)) + +const a: string = foo[dashStrMem]; +>a : Symbol(a, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 14, 5)) +>foo : Symbol(foo, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 0), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 0, 24), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 2, 26), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 4, 28), Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 36) ... and 1 more) +>dashStrMem : Symbol(dashStrMem, Decl(lateBoundElementAccessAssignmentDeclarations.ts, 6, 5)) + diff --git a/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.types b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.types new file mode 100644 index 0000000000000..2175a04ecfc96 --- /dev/null +++ b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarations.types @@ -0,0 +1,80 @@ +=== tests/cases/compiler/lateBoundElementAccessAssignmentDeclarations.ts === +export function foo() {} +>foo : typeof foo + +foo.bar = 12; +>foo.bar = 12 : 12 +>foo.bar : number +>foo : typeof foo +>bar : number +>12 : 12 + +const _private = Symbol(); +>_private : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +foo[_private] = "ok"; +>foo[_private] = "ok" : "ok" +>foo[_private] : string +>foo : typeof foo +>_private : unique symbol +>"ok" : "ok" + +const strMem = "strMemName"; +>strMem : "strMemName" +>"strMemName" : "strMemName" + +foo[strMem] = "ok"; +>foo[strMem] = "ok" : "ok" +>foo[strMem] : string +>foo : typeof foo +>strMem : "strMemName" +>"ok" : "ok" + +const dashStrMem = "dashed-str-mem"; +>dashStrMem : "dashed-str-mem" +>"dashed-str-mem" : "dashed-str-mem" + +foo[dashStrMem] = "ok"; +>foo[dashStrMem] = "ok" : "ok" +>foo[dashStrMem] : string +>foo : typeof foo +>dashStrMem : "dashed-str-mem" +>"ok" : "ok" + +const numMem = 42; +>numMem : 42 +>42 : 42 + +foo[numMem] = "ok"; +>foo[numMem] = "ok" : "ok" +>foo[numMem] : string +>foo : typeof foo +>numMem : 42 +>"ok" : "ok" + +const x: string = foo[_private]; +>x : string +>foo[_private] : string +>foo : typeof foo +>_private : unique symbol + +const y: string = foo[strMem]; +>y : string +>foo[strMem] : string +>foo : typeof foo +>strMem : "strMemName" + +const z: string = foo[numMem]; +>z : string +>foo[numMem] : string +>foo : typeof foo +>numMem : 42 + +const a: string = foo[dashStrMem]; +>a : string +>foo[dashStrMem] : string +>foo : typeof foo +>dashStrMem : "dashed-str-mem" + diff --git a/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.js b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.js new file mode 100644 index 0000000000000..a9b6a0a86cdfa --- /dev/null +++ b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.js @@ -0,0 +1,29 @@ +//// [file.js] +export function foo() {} +foo.bar = 12; +const _private = Symbol(); +foo[_private] = "ok"; +const strMem = "strMemName"; +foo[strMem] = "ok"; +const dashStrMem = "dashed-str-mem"; +foo[dashStrMem] = "ok"; +const numMem = 42; +foo[numMem] = "ok"; + +/** @type {string} */ +const x = foo[_private]; +/** @type {string} */ +const y = foo[strMem]; +/** @type {string} */ +const z = foo[numMem]; +/** @type {string} */ +const a = foo[dashStrMem]; + + + +//// [file.d.ts] +export function foo(): void; +export namespace foo { + export const bar: number; + export const strMemName: string; +} diff --git a/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.symbols b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.symbols new file mode 100644 index 0000000000000..669ed588b19b2 --- /dev/null +++ b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.symbols @@ -0,0 +1,62 @@ +=== tests/cases/compiler/file.js === +export function foo() {} +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) + +foo.bar = 12; +>foo.bar : Symbol(foo.bar, Decl(file.js, 0, 24)) +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>bar : Symbol(foo.bar, Decl(file.js, 0, 24)) + +const _private = Symbol(); +>_private : Symbol(_private, Decl(file.js, 2, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +foo[_private] = "ok"; +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>_private : Symbol(_private, Decl(file.js, 2, 5)) + +const strMem = "strMemName"; +>strMem : Symbol(strMem, Decl(file.js, 4, 5)) + +foo[strMem] = "ok"; +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>strMem : Symbol(strMem, Decl(file.js, 4, 5)) + +const dashStrMem = "dashed-str-mem"; +>dashStrMem : Symbol(dashStrMem, Decl(file.js, 6, 5)) + +foo[dashStrMem] = "ok"; +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>dashStrMem : Symbol(dashStrMem, Decl(file.js, 6, 5)) + +const numMem = 42; +>numMem : Symbol(numMem, Decl(file.js, 8, 5)) + +foo[numMem] = "ok"; +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>numMem : Symbol(numMem, Decl(file.js, 8, 5)) + +/** @type {string} */ +const x = foo[_private]; +>x : Symbol(x, Decl(file.js, 12, 5)) +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>_private : Symbol(_private, Decl(file.js, 2, 5)) + +/** @type {string} */ +const y = foo[strMem]; +>y : Symbol(y, Decl(file.js, 14, 5)) +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>strMem : Symbol(strMem, Decl(file.js, 4, 5)) + +/** @type {string} */ +const z = foo[numMem]; +>z : Symbol(z, Decl(file.js, 16, 5)) +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>numMem : Symbol(numMem, Decl(file.js, 8, 5)) + +/** @type {string} */ +const a = foo[dashStrMem]; +>a : Symbol(a, Decl(file.js, 18, 5)) +>foo : Symbol(foo, Decl(file.js, 0, 0), Decl(file.js, 0, 24), Decl(file.js, 2, 26), Decl(file.js, 4, 28), Decl(file.js, 6, 36) ... and 1 more) +>dashStrMem : Symbol(dashStrMem, Decl(file.js, 6, 5)) + diff --git a/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.types b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.types new file mode 100644 index 0000000000000..d2c80bf4a5897 --- /dev/null +++ b/tests/baselines/reference/lateBoundElementAccessAssignmentDeclarationsInJs.types @@ -0,0 +1,84 @@ +=== tests/cases/compiler/file.js === +export function foo() {} +>foo : typeof foo + +foo.bar = 12; +>foo.bar = 12 : 12 +>foo.bar : number +>foo : typeof foo +>bar : number +>12 : 12 + +const _private = Symbol(); +>_private : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +foo[_private] = "ok"; +>foo[_private] = "ok" : "ok" +>foo[_private] : string +>foo : typeof foo +>_private : unique symbol +>"ok" : "ok" + +const strMem = "strMemName"; +>strMem : "strMemName" +>"strMemName" : "strMemName" + +foo[strMem] = "ok"; +>foo[strMem] = "ok" : "ok" +>foo[strMem] : string +>foo : typeof foo +>strMem : "strMemName" +>"ok" : "ok" + +const dashStrMem = "dashed-str-mem"; +>dashStrMem : "dashed-str-mem" +>"dashed-str-mem" : "dashed-str-mem" + +foo[dashStrMem] = "ok"; +>foo[dashStrMem] = "ok" : "ok" +>foo[dashStrMem] : string +>foo : typeof foo +>dashStrMem : "dashed-str-mem" +>"ok" : "ok" + +const numMem = 42; +>numMem : 42 +>42 : 42 + +foo[numMem] = "ok"; +>foo[numMem] = "ok" : "ok" +>foo[numMem] : string +>foo : typeof foo +>numMem : 42 +>"ok" : "ok" + +/** @type {string} */ +const x = foo[_private]; +>x : string +>foo[_private] : string +>foo : typeof foo +>_private : unique symbol + +/** @type {string} */ +const y = foo[strMem]; +>y : string +>foo[strMem] : string +>foo : typeof foo +>strMem : "strMemName" + +/** @type {string} */ +const z = foo[numMem]; +>z : string +>foo[numMem] : string +>foo : typeof foo +>numMem : 42 + +/** @type {string} */ +const a = foo[dashStrMem]; +>a : string +>foo[dashStrMem] : string +>foo : typeof foo +>dashStrMem : "dashed-str-mem" + diff --git a/tests/cases/compiler/lateBoundElementAccessAssignmentDeclarations.ts b/tests/cases/compiler/lateBoundElementAccessAssignmentDeclarations.ts new file mode 100644 index 0000000000000..fc90e9486bb6c --- /dev/null +++ b/tests/cases/compiler/lateBoundElementAccessAssignmentDeclarations.ts @@ -0,0 +1,18 @@ +// @strict: true +// @declaration: true +// @target: es6 +export function foo() {} +foo.bar = 12; +const _private = Symbol(); +foo[_private] = "ok"; +const strMem = "strMemName"; +foo[strMem] = "ok"; +const dashStrMem = "dashed-str-mem"; +foo[dashStrMem] = "ok"; +const numMem = 42; +foo[numMem] = "ok"; + +const x: string = foo[_private]; +const y: string = foo[strMem]; +const z: string = foo[numMem]; +const a: string = foo[dashStrMem]; \ No newline at end of file diff --git a/tests/cases/compiler/lateBoundElementAccessAssignmentDeclarationsInJs.ts b/tests/cases/compiler/lateBoundElementAccessAssignmentDeclarationsInJs.ts new file mode 100644 index 0000000000000..878d05ffa0a5a --- /dev/null +++ b/tests/cases/compiler/lateBoundElementAccessAssignmentDeclarationsInJs.ts @@ -0,0 +1,26 @@ +// @strict: true +// @declaration: true +// @target: es6 +// @checkJs: true +// @allowJs: true +// @emitDeclarationOnly: true +// @filename: file.js +export function foo() {} +foo.bar = 12; +const _private = Symbol(); +foo[_private] = "ok"; +const strMem = "strMemName"; +foo[strMem] = "ok"; +const dashStrMem = "dashed-str-mem"; +foo[dashStrMem] = "ok"; +const numMem = 42; +foo[numMem] = "ok"; + +/** @type {string} */ +const x = foo[_private]; +/** @type {string} */ +const y = foo[strMem]; +/** @type {string} */ +const z = foo[numMem]; +/** @type {string} */ +const a = foo[dashStrMem]; \ No newline at end of file