Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2521,7 +2521,49 @@ namespace ts {
return getMergedSymbol(symbol.parent && getLateBoundSymbol(symbol.parent));
}

function getExportSymbolOfValueSymbolIfExported(symbol: Symbol): Symbol {
/**
* 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 {
return symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0
? getMergedSymbol(symbol.exportSymbol)
: symbol;
Expand Down Expand Up @@ -2824,7 +2866,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
Expand Down Expand Up @@ -3711,12 +3753,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]);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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))

Original file line number Diff line number Diff line change
@@ -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

4 changes: 2 additions & 2 deletions tests/baselines/reference/importTypeGenericTypes.types
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/importTypeLocal.types
Original file line number Diff line number Diff line change
Expand Up @@ -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
>"" : ""
Expand Down
Original file line number Diff line number Diff line change
@@ -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();