mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
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:
parent
34c6b1a76b
commit
b63ea4b6df
@ -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)
|
||||
|
||||
@ -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]: {};
|
||||
};
|
||||
26
tests/cases/compiler/uniqueSymbolPropertyDeclarationEmit.ts
Normal file
26
tests/cases/compiler/uniqueSymbolPropertyDeclarationEmit.ts
Normal 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;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user