For type emit, walk non-parent containers when those containers have aliases leading to the target (#24507)

This commit is contained in:
Wesley Wigham
2018-05-30 17:52:59 -07:00
committed by GitHub
parent 2cb46407b1
commit 576a733378
7 changed files with 149 additions and 7 deletions

View File

@@ -2533,6 +2533,46 @@ namespace ts {
return getMergedSymbol(symbol.parent && getLateBoundSymbol(symbol.parent));
}
/**
* 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 getContainerOfSymbol(symbol: Symbol): Symbol | undefined {
const container = getParentOfSymbol(symbol);
if (container) {
return container;
}
const candidate = forEach(symbol.declarations, d => !isAmbientModule(d) && d.parent && hasNonGlobalAugmentationExternalModuleSymbol(d.parent) ? getSymbolOfNode(d.parent) : undefined);
if (!candidate) {
return undefined;
}
const alias = getAliasForSymbolInContainer(candidate, symbol);
return alias ? candidate : undefined;
}
function getAliasForSymbolInContainer(container: Symbol, symbol: Symbol) {
if (container === getParentOfSymbol(symbol)) {
// fast path, `symbol` is either already the alias or isn't aliased
return symbol;
}
const exports = getExportsOfSymbol(container);
const quick = exports.get(symbol.escapedName);
if (quick && symbolRefersToTarget(quick)) {
return quick;
}
return forEachEntry(exports, exported => {
if (symbolRefersToTarget(exported)) {
return exported;
}
});
function symbolRefersToTarget(s: Symbol) {
if (s === symbol || resolveSymbol(s) === symbol || resolveSymbol(s) === resolveSymbol(symbol)) {
return s;
}
}
}
function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol;
function getExportSymbolOfValueSymbolIfExported(symbol: Symbol | undefined): Symbol | undefined;
function getExportSymbolOfValueSymbolIfExported(symbol: Symbol | undefined): Symbol | undefined {
@@ -2838,7 +2878,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
meaningToLook = getQualifiedLeftMeaning(meaning);
symbol = getParentOfSymbol(symbol);
symbol = getContainerOfSymbol(symbol);
}
// This could be a symbol that is not exported in the external module
@@ -3729,12 +3769,12 @@ namespace ts {
needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
// Go up and add our parent.
const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol);
const parent = getContainerOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol);
if (parent) {
const parentChain = getSymbolChain(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false);
if (parentChain) {
parentSymbol = parent;
accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [symbol]);
accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [getAliasForSymbolInContainer(parent, symbol) || symbol]);
}
}
}

View File

@@ -0,0 +1,37 @@
//// [tests/cases/compiler/allowSyntheticDefaultImportsCanPaintCrossModuleDeclaration.ts] ////
//// [color.ts]
interface Color {
c: string;
}
export default Color;
//// [file1.ts]
import Color from "./color";
export declare function styled(): Color;
//// [file2.ts]
import { styled } from "./file1";
export const A = styled();
//// [color.js]
"use strict";
exports.__esModule = true;
//// [file1.js]
"use strict";
exports.__esModule = true;
//// [file2.js]
"use strict";
exports.__esModule = true;
var file1_1 = require("./file1");
exports.A = file1_1.styled();
//// [color.d.ts]
interface Color {
c: string;
}
export default Color;
//// [file1.d.ts]
import Color from "./color";
export declare function styled(): Color;
//// [file2.d.ts]
export declare const A: import("./color").default;

View File

@@ -0,0 +1,26 @@
=== tests/cases/compiler/color.ts ===
interface Color {
>Color : Symbol(Color, Decl(color.ts, 0, 0))
c: string;
>c : Symbol(Color.c, Decl(color.ts, 0, 17))
}
export default Color;
>Color : Symbol(Color, Decl(color.ts, 0, 0))
=== tests/cases/compiler/file1.ts ===
import Color from "./color";
>Color : Symbol(Color, Decl(file1.ts, 0, 6))
export declare function styled(): Color;
>styled : Symbol(styled, Decl(file1.ts, 0, 28))
>Color : Symbol(Color, Decl(file1.ts, 0, 6))
=== tests/cases/compiler/file2.ts ===
import { styled } from "./file1";
>styled : Symbol(styled, Decl(file2.ts, 0, 8))
export const A = styled();
>A : Symbol(A, Decl(file2.ts, 1, 12))
>styled : Symbol(styled, Decl(file2.ts, 0, 8))

View File

@@ -0,0 +1,27 @@
=== tests/cases/compiler/color.ts ===
interface Color {
>Color : Color
c: string;
>c : string
}
export default Color;
>Color : Color
=== tests/cases/compiler/file1.ts ===
import Color from "./color";
>Color : any
export declare function styled(): Color;
>styled : () => Color
>Color : Color
=== tests/cases/compiler/file2.ts ===
import { styled } from "./file1";
>styled : () => import("tests/cases/compiler/color").default
export const A = styled();
>A : import("tests/cases/compiler/color").default
>styled() : import("tests/cases/compiler/color").default
>styled : () => import("tests/cases/compiler/color").default

View File

@@ -89,9 +89,9 @@ export const x: import("./foo")<{x: number}> = { x: 0, y: 0, data: {x: 12} };
>12 : 12
export let y: import("./foo2").Bar.I<{x: number}> = { a: "", b: 0, data: {x: 12} };
>y : Bar.I<{ x: number; }>
>y : import("tests/cases/conformance/types/import/foo2").Bar.I<{ x: number; }>
>Bar : any
>I : Bar.I<T>
>I : import("tests/cases/conformance/types/import/foo2").Bar.I<T>
>x : number
>{ a: "", b: 0, data: {x: 12} } : { a: string; b: number; data: { x: number; }; }
>a : string

View File

@@ -66,9 +66,9 @@ export const x: import("./foo") = { x: 0, y: 0 };
>0 : 0
export let y: import("./foo2").Bar.I = { a: "", b: 0 };
>y : Bar.I
>y : import("tests/cases/conformance/types/import/foo2").Bar.I
>Bar : any
>I : Bar.I
>I : import("tests/cases/conformance/types/import/foo2").Bar.I
>{ a: "", b: 0 } : { a: string; b: number; }
>a : string
>"" : ""

View File

@@ -0,0 +1,12 @@
// @declaration: true
// @filename: color.ts
interface Color {
c: string;
}
export default Color;
// @filename: file1.ts
import Color from "./color";
export declare function styled(): Color;
// @filename: file2.ts
import { styled } from "./file1";
export const A = styled();