Fix declaration emit for property references of imported object literal types (#39055)

* Fix declaration emit for property references of imported object literal types

* Add declaration file to test
This commit is contained in:
Andrew Branch 2020-06-12 17:48:19 -07:00 committed by GitHub
parent 34c6b1a76b
commit b63ea4b6df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 104 additions and 14 deletions

View File

@ -3488,16 +3488,17 @@ namespace ts {
* Attempts to find the symbol corresponding to the container a symbol is in - usually this
* is just its' `.parent`, but for locals, this value is `undefined`
*/
function getContainersOfSymbol(symbol: Symbol, enclosingDeclaration: Node | undefined): Symbol[] | undefined {
function getContainersOfSymbol(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): Symbol[] | undefined {
const container = getParentOfSymbol(symbol);
// Type parameters end up in the `members` lists but are not externally visible
if (container && !(symbol.flags & SymbolFlags.TypeParameter)) {
const additionalContainers = mapDefined(container.declarations, fileSymbolIfFileSymbolExportEqualsContainer);
const reexportContainers = enclosingDeclaration && getAlternativeContainingModules(symbol, enclosingDeclaration);
const objectLiteralContainer = getVariableDeclarationOfObjectLiteral(container, meaning);
if (enclosingDeclaration && getAccessibleSymbolChain(container, enclosingDeclaration, SymbolFlags.Namespace, /*externalOnly*/ false)) {
return concatenate(concatenate([container], additionalContainers), reexportContainers); // This order expresses a preference for the real container if it is in scope
return append(concatenate(concatenate([container], additionalContainers), reexportContainers), objectLiteralContainer); // This order expresses a preference for the real container if it is in scope
}
const res = append(additionalContainers, container);
const res = append(append(additionalContainers, container), objectLiteralContainer);
return concatenate(res, reexportContainers);
}
const candidates = mapDefined(symbol.declarations, d => {
@ -3522,6 +3523,18 @@ namespace ts {
}
}
function getVariableDeclarationOfObjectLiteral(symbol: Symbol, meaning: SymbolFlags) {
// If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct
// from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however,
// we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal.
const firstDecl: Node | false = !!length(symbol.declarations) && first(symbol.declarations);
if (meaning & SymbolFlags.Value && firstDecl && firstDecl.parent && isVariableDeclaration(firstDecl.parent)) {
if (isObjectLiteralExpression(firstDecl) && firstDecl === firstDecl.parent.initializer || isTypeLiteralNode(firstDecl) && firstDecl === firstDecl.parent.type) {
return getSymbolOfNode(firstDecl.parent);
}
}
}
function getFileSymbolIfFileSymbolExportEqualsContainer(d: Declaration, container: Symbol) {
const fileSymbol = getExternalModuleContainer(d);
const exported = fileSymbol && fileSymbol.exports && fileSymbol.exports.get(InternalSymbolName.ExportEquals);
@ -3915,16 +3928,7 @@ namespace ts {
// But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible
// It is accessible if the parent m is accessible because then m.c can be accessed through qualification
let containers = getContainersOfSymbol(symbol, enclosingDeclaration);
// If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct
// from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however,
// we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal.
const firstDecl: Node | false = !!length(symbol.declarations) && first(symbol.declarations);
if (!length(containers) && meaning & SymbolFlags.Value && firstDecl && isObjectLiteralExpression(firstDecl)) {
if (firstDecl.parent && isVariableDeclaration(firstDecl.parent) && firstDecl === firstDecl.parent.initializer) {
containers = [getSymbolOfNode(firstDecl.parent)];
}
}
const containers = getContainersOfSymbol(symbol, enclosingDeclaration, meaning);
const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible, allowModules);
if (parentResult) {
return parentResult;
@ -5071,7 +5075,7 @@ namespace ts {
needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent.
const parents = getContainersOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol, context.enclosingDeclaration);
const parents = getContainersOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol, context.enclosingDeclaration, meaning);
if (length(parents)) {
parentSpecifiers = parents!.map(symbol =>
some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)

View File

@ -0,0 +1,60 @@
//// [tests/cases/compiler/uniqueSymbolPropertyDeclarationEmit.ts] ////
//// [test.ts]
import Op from './op';
import { Po } from './po';
export default function foo() {
return {
[Op.or]: [],
[Po.ro]: {}
};
}
//// [op.ts]
declare const Op: {
readonly or: unique symbol;
};
export default Op;
//// [po.d.ts]
export declare const Po: {
readonly ro: unique symbol;
};
//// [op.js]
"use strict";
exports.__esModule = true;
exports["default"] = Op;
//// [test.js]
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
exports.__esModule = true;
var op_1 = __importDefault(require("./op"));
var po_1 = require("./po");
function foo() {
var _a;
return _a = {},
_a[op_1["default"].or] = [],
_a[po_1.Po.ro] = {},
_a;
}
exports["default"] = foo;
//// [op.d.ts]
declare const Op: {
readonly or: unique symbol;
};
export default Op;
//// [test.d.ts]
import Op from './op';
import { Po } from './po';
export default function foo(): {
[Op.or]: any[];
[Po.ro]: {};
};

View File

@ -0,0 +1,26 @@
// @noTypesAndSymbols: true
// @esModuleInterop: true
// @declaration: true
// @Filename: test.ts
import Op from './op';
import { Po } from './po';
export default function foo() {
return {
[Op.or]: [],
[Po.ro]: {}
};
}
// @Filename: op.ts
declare const Op: {
readonly or: unique symbol;
};
export default Op;
// @Filename: po.d.ts
export declare const Po: {
readonly ro: unique symbol;
};