From 960d114b6b746596d316bfef4f3d91e41110cd81 Mon Sep 17 00:00:00 2001 From: Wesley Wigham <wewigham@microsoft.com> Date: Fri, 10 Nov 2017 13:39:42 -0800 Subject: [PATCH 1/5] Do visibility painting from collectLinkedAliases in checker to remove statefullness in declaration emit --- src/compiler/checker.ts | 17 ++++++++++---- .../mutuallyRecursiveInterfaceDeclaration.js | 23 +++++++++++++++++++ ...uallyRecursiveInterfaceDeclaration.symbols | 20 ++++++++++++++++ ...utuallyRecursiveInterfaceDeclaration.types | 20 ++++++++++++++++ .../mutuallyRecursiveInterfaceDeclaration.ts | 9 ++++++++ 5 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.js create mode 100644 tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.symbols create mode 100644 tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.types create mode 100644 tests/cases/compiler/mutuallyRecursiveInterfaceDeclaration.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 999934e351c1a..281f0ec922cbc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4046,15 +4046,15 @@ namespace ts { } } - function collectLinkedAliases(node: Identifier): Node[] { + function collectLinkedAliases(node: Identifier, setVisibility?: boolean): Node[] | undefined { let exportSymbol: Symbol; if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) { - exportSymbol = resolveName(node.parent, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node, /*isUse*/ false); + exportSymbol = resolveName(node, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, node, /*isUse*/ false); } else if (node.parent.kind === SyntaxKind.ExportSpecifier) { exportSymbol = getTargetOfExportSpecifier(<ExportSpecifier>node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } - const result: Node[] = []; + let result: Node[]; if (exportSymbol) { buildVisibleNodeList(exportSymbol.declarations); } @@ -4062,9 +4062,14 @@ namespace ts { function buildVisibleNodeList(declarations: Declaration[]) { forEach(declarations, declaration => { - getNodeLinks(declaration).isVisible = true; const resultNode = getAnyImportSyntax(declaration) || declaration; - pushIfUnique(result, resultNode); + if (setVisibility) { + getNodeLinks(declaration).isVisible = true; + } + else { + result = result || []; + pushIfUnique(result, resultNode); + } if (isInternalModuleImportEqualsDeclaration(declaration)) { // Add the referenced top container visible @@ -23258,6 +23263,7 @@ namespace ts { function checkExportSpecifier(node: ExportSpecifier) { checkAliasSymbol(node); + collectLinkedAliases(node.propertyName || node.name, /*setVisibility*/ true); // Collect linked aliases to set visibility links if (!(<ExportDeclaration>node.parent.parent).moduleSpecifier) { const exportedName = node.propertyName || node.name; // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) @@ -23295,6 +23301,7 @@ namespace ts { } if (node.expression.kind === SyntaxKind.Identifier) { markExportAsReferenced(node); + collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true); // Sets visibility flags on refernced nodes } else { checkExpressionCached(node.expression); diff --git a/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.js b/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.js new file mode 100644 index 0000000000000..567cb71cafb5a --- /dev/null +++ b/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.js @@ -0,0 +1,23 @@ +//// [mutuallyRecursiveInterfaceDeclaration.ts] +interface A { + b: B +} + +interface B { + a: A +} +export {A, B} + +//// [mutuallyRecursiveInterfaceDeclaration.js] +"use strict"; +exports.__esModule = true; + + +//// [mutuallyRecursiveInterfaceDeclaration.d.ts] +interface A { + b: B; +} +interface B { + a: A; +} +export { A, B }; diff --git a/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.symbols b/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.symbols new file mode 100644 index 0000000000000..b0ac456859c9b --- /dev/null +++ b/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/mutuallyRecursiveInterfaceDeclaration.ts === +interface A { +>A : Symbol(A, Decl(mutuallyRecursiveInterfaceDeclaration.ts, 0, 0)) + + b: B +>b : Symbol(A.b, Decl(mutuallyRecursiveInterfaceDeclaration.ts, 0, 13)) +>B : Symbol(B, Decl(mutuallyRecursiveInterfaceDeclaration.ts, 2, 1)) +} + +interface B { +>B : Symbol(B, Decl(mutuallyRecursiveInterfaceDeclaration.ts, 2, 1)) + + a: A +>a : Symbol(B.a, Decl(mutuallyRecursiveInterfaceDeclaration.ts, 4, 13)) +>A : Symbol(A, Decl(mutuallyRecursiveInterfaceDeclaration.ts, 0, 0)) +} +export {A, B} +>A : Symbol(A, Decl(mutuallyRecursiveInterfaceDeclaration.ts, 7, 8)) +>B : Symbol(B, Decl(mutuallyRecursiveInterfaceDeclaration.ts, 7, 10)) + diff --git a/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.types b/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.types new file mode 100644 index 0000000000000..807687160b241 --- /dev/null +++ b/tests/baselines/reference/mutuallyRecursiveInterfaceDeclaration.types @@ -0,0 +1,20 @@ +=== tests/cases/compiler/mutuallyRecursiveInterfaceDeclaration.ts === +interface A { +>A : A + + b: B +>b : B +>B : B +} + +interface B { +>B : B + + a: A +>a : A +>A : A +} +export {A, B} +>A : any +>B : any + diff --git a/tests/cases/compiler/mutuallyRecursiveInterfaceDeclaration.ts b/tests/cases/compiler/mutuallyRecursiveInterfaceDeclaration.ts new file mode 100644 index 0000000000000..e9609605c6925 --- /dev/null +++ b/tests/cases/compiler/mutuallyRecursiveInterfaceDeclaration.ts @@ -0,0 +1,9 @@ +// @declaration: true +interface A { + b: B +} + +interface B { + a: A +} +export {A, B} \ No newline at end of file From c888ea3a8318a0a9cb325d1cf71ff4eee3ef42ba Mon Sep 17 00:00:00 2001 From: Wesley Wigham <wewigham@microsoft.com> Date: Fri, 17 Nov 2017 13:54:57 -0800 Subject: [PATCH 2/5] Fix #17085 --- src/compiler/declarationEmitter.ts | 18 +++++--- .../reference/destructuredDeclarationEmit.js | 44 +++++++++++++++++++ .../destructuredDeclarationEmit.symbols | 36 +++++++++++++++ .../destructuredDeclarationEmit.types | 43 ++++++++++++++++++ .../compiler/destructuredDeclarationEmit.ts | 10 +++++ 5 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/destructuredDeclarationEmit.js create mode 100644 tests/baselines/reference/destructuredDeclarationEmit.symbols create mode 100644 tests/baselines/reference/destructuredDeclarationEmit.types create mode 100644 tests/cases/compiler/destructuredDeclarationEmit.ts diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index ebd2985dd45c2..d629f6b450924 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1249,10 +1249,18 @@ namespace ts { writeLine(); } + function bindingNameContainsVisibleBindingElement(node: BindingName): boolean { + return !!node && isBindingPattern(node) && some(node.elements, elem => !isOmittedExpression(elem) && (resolver.isDeclarationVisible(elem) || bindingNameContainsVisibleBindingElement(elem.name))); + } + + function isVariableDeclarationVisible(node: VariableDeclaration | BindingElement) { + return resolver.isDeclarationVisible(node) || bindingNameContainsVisibleBindingElement(node.name); + } + function emitVariableDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration) { // If we are emitting property it isn't moduleElement and hence we already know it needs to be emitted // so there is no check needed to see if declaration is visible - if (node.kind !== SyntaxKind.VariableDeclaration || resolver.isDeclarationVisible(node)) { + if (node.kind !== SyntaxKind.VariableDeclaration || isVariableDeclarationVisible(node)) { if (isBindingPattern(node.name)) { emitBindingPattern(<BindingPattern>node.name); } @@ -1324,14 +1332,14 @@ namespace ts { } function emitBindingPattern(bindingPattern: BindingPattern) { - // Only select non-omitted expression from the bindingPattern's elements. + // Only select visible, non-omitted expression from the bindingPattern's elements. // We have to do this to avoid emitting trailing commas. // For example: // original: var [, c,,] = [ 2,3,4] // emitted: declare var c: number; // instead of declare var c:number, ; const elements: Node[] = []; for (const element of bindingPattern.elements) { - if (element.kind !== SyntaxKind.OmittedExpression) { + if (element.kind !== SyntaxKind.OmittedExpression && isVariableDeclarationVisible(element)) { elements.push(element); } } @@ -1371,7 +1379,7 @@ namespace ts { } function isVariableStatementVisible(node: VariableStatement) { - return forEach(node.declarationList.declarations, varDeclaration => resolver.isDeclarationVisible(varDeclaration)); + return forEach(node.declarationList.declarations, varDeclaration => isVariableDeclarationVisible(varDeclaration)); } function writeVariableStatement(node: VariableStatement) { @@ -1390,7 +1398,7 @@ namespace ts { else { write("var "); } - emitCommaList(node.declarationList.declarations, emitVariableDeclaration, resolver.isDeclarationVisible); + emitCommaList(node.declarationList.declarations, emitVariableDeclaration, isVariableDeclarationVisible); write(";"); writeLine(); } diff --git a/tests/baselines/reference/destructuredDeclarationEmit.js b/tests/baselines/reference/destructuredDeclarationEmit.js new file mode 100644 index 0000000000000..8ba01e7a7c1ef --- /dev/null +++ b/tests/baselines/reference/destructuredDeclarationEmit.js @@ -0,0 +1,44 @@ +//// [tests/cases/compiler/destructuredDeclarationEmit.ts] //// + +//// [foo.ts] +const foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } }; +export { foo }; +//// [index.ts] +import { foo } from './foo'; +export { foo }; + +const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; +export { baz, ibaz }; + +//// [foo.js] +"use strict"; +exports.__esModule = true; +var foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } }; +exports.foo = foo; +//// [index.js] +"use strict"; +exports.__esModule = true; +var foo_1 = require("./foo"); +exports.foo = foo_1.foo; +var baz = foo_1.foo.bar, bat = foo_1.foo.bat, _a = foo_1.foo.bam.bork, ibar = _a.bar, ibaz = _a.baz; +exports.baz = baz; +exports.ibaz = ibaz; + + +//// [foo.d.ts] +declare const foo: { + bar: string; + bat: string; + bam: { + bork: { + bar: string; + baz: string; + }; + }; +}; +export { foo }; +//// [index.d.ts] +import { foo } from './foo'; +export { foo }; +declare const baz: string, ibaz: string; +export { baz, ibaz }; diff --git a/tests/baselines/reference/destructuredDeclarationEmit.symbols b/tests/baselines/reference/destructuredDeclarationEmit.symbols new file mode 100644 index 0000000000000..2ec953881666a --- /dev/null +++ b/tests/baselines/reference/destructuredDeclarationEmit.symbols @@ -0,0 +1,36 @@ +=== tests/cases/compiler/foo.ts === +const foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } }; +>foo : Symbol(foo, Decl(foo.ts, 0, 5)) +>bar : Symbol(bar, Decl(foo.ts, 0, 13)) +>bat : Symbol(bat, Decl(foo.ts, 0, 27)) +>bam : Symbol(bam, Decl(foo.ts, 0, 41)) +>bork : Symbol(bork, Decl(foo.ts, 0, 48)) +>bar : Symbol(bar, Decl(foo.ts, 0, 56)) +>baz : Symbol(baz, Decl(foo.ts, 0, 66)) + +export { foo }; +>foo : Symbol(foo, Decl(foo.ts, 1, 8)) + +=== tests/cases/compiler/index.ts === +import { foo } from './foo'; +>foo : Symbol(foo, Decl(index.ts, 0, 8)) + +export { foo }; +>foo : Symbol(foo, Decl(index.ts, 1, 8)) + +const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; +>bar : Symbol(bar, Decl(foo.ts, 0, 13)) +>baz : Symbol(baz, Decl(index.ts, 3, 7)) +>bat : Symbol(bat, Decl(index.ts, 3, 17)) +>bam : Symbol(bam, Decl(foo.ts, 0, 41)) +>bork : Symbol(bork, Decl(foo.ts, 0, 48)) +>bar : Symbol(bar, Decl(foo.ts, 0, 56)) +>ibar : Symbol(ibar, Decl(index.ts, 3, 37)) +>baz : Symbol(baz, Decl(foo.ts, 0, 66)) +>ibaz : Symbol(ibaz, Decl(index.ts, 3, 48)) +>foo : Symbol(foo, Decl(index.ts, 0, 8)) + +export { baz, ibaz }; +>baz : Symbol(baz, Decl(index.ts, 4, 8)) +>ibaz : Symbol(ibaz, Decl(index.ts, 4, 13)) + diff --git a/tests/baselines/reference/destructuredDeclarationEmit.types b/tests/baselines/reference/destructuredDeclarationEmit.types new file mode 100644 index 0000000000000..5f991d39dbf65 --- /dev/null +++ b/tests/baselines/reference/destructuredDeclarationEmit.types @@ -0,0 +1,43 @@ +=== tests/cases/compiler/foo.ts === +const foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } }; +>foo : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } +>{ bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } } : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } +>bar : string +>'hello' : "hello" +>bat : string +>'world' : "world" +>bam : { bork: { bar: string; baz: string; }; } +>{ bork: { bar: 'a', baz: 'b' } } : { bork: { bar: string; baz: string; }; } +>bork : { bar: string; baz: string; } +>{ bar: 'a', baz: 'b' } : { bar: string; baz: string; } +>bar : string +>'a' : "a" +>baz : string +>'b' : "b" + +export { foo }; +>foo : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } + +=== tests/cases/compiler/index.ts === +import { foo } from './foo'; +>foo : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } + +export { foo }; +>foo : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } + +const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; +>bar : any +>baz : string +>bat : string +>bam : any +>bork : any +>bar : any +>ibar : string +>baz : any +>ibaz : string +>foo : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } + +export { baz, ibaz }; +>baz : string +>ibaz : string + diff --git a/tests/cases/compiler/destructuredDeclarationEmit.ts b/tests/cases/compiler/destructuredDeclarationEmit.ts new file mode 100644 index 0000000000000..f7f9f67af14ec --- /dev/null +++ b/tests/cases/compiler/destructuredDeclarationEmit.ts @@ -0,0 +1,10 @@ +// @declaration: true +// @filename: foo.ts +const foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } }; +export { foo }; +// @filename: index.ts +import { foo } from './foo'; +export { foo }; + +const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; +export { baz, ibaz }; \ No newline at end of file From 18962c1ed976ed74243989e401345a057c8f57bf Mon Sep 17 00:00:00 2001 From: Wesley Wigham <wewigham@microsoft.com> Date: Fri, 17 Nov 2017 14:07:30 -0800 Subject: [PATCH 3/5] Add deeply destructured array to test --- .../reference/destructuredDeclarationEmit.js | 32 ++++++++++--- .../destructuredDeclarationEmit.symbols | 29 ++++++++++-- .../destructuredDeclarationEmit.types | 45 +++++++++++++++++-- .../compiler/destructuredDeclarationEmit.ts | 12 +++-- 4 files changed, 100 insertions(+), 18 deletions(-) diff --git a/tests/baselines/reference/destructuredDeclarationEmit.js b/tests/baselines/reference/destructuredDeclarationEmit.js index 8ba01e7a7c1ef..4ae4cbfc35603 100644 --- a/tests/baselines/reference/destructuredDeclarationEmit.js +++ b/tests/baselines/reference/destructuredDeclarationEmit.js @@ -2,27 +2,38 @@ //// [foo.ts] const foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } }; -export { foo }; +const arr: [0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]] = [0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]]; +export { foo, arr }; //// [index.ts] -import { foo } from './foo'; -export { foo }; +import { foo, arr } from './foo'; +export { foo, arr }; const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; -export { baz, ibaz }; +export { baz, ibaz }; + +const [ , one, , [, bee, , [, {sec} ]]] = arr; +export { one, bee, sec }; //// [foo.js] "use strict"; exports.__esModule = true; var foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } }; exports.foo = foo; +var arr = [0, 1, 2, ['a', 'b', 'c', [{ def: 'def' }, { sec: 'sec' }]]]; +exports.arr = arr; //// [index.js] "use strict"; exports.__esModule = true; var foo_1 = require("./foo"); exports.foo = foo_1.foo; +exports.arr = foo_1.arr; var baz = foo_1.foo.bar, bat = foo_1.foo.bat, _a = foo_1.foo.bam.bork, ibar = _a.bar, ibaz = _a.baz; exports.baz = baz; exports.ibaz = ibaz; +var one = foo_1.arr[1], _b = foo_1.arr[3], bee = _b[1], _c = _b[3], sec = _c[1].sec; +exports.one = one; +exports.bee = bee; +exports.sec = sec; //// [foo.d.ts] @@ -36,9 +47,16 @@ declare const foo: { }; }; }; -export { foo }; +declare const arr: [0, 1, 2, ['a', 'b', 'c', [{ + def: 'def'; +}, { + sec: 'sec'; +}]]]; +export { foo, arr }; //// [index.d.ts] -import { foo } from './foo'; -export { foo }; +import { foo, arr } from './foo'; +export { foo, arr }; declare const baz: string, ibaz: string; export { baz, ibaz }; +declare const one: 1, bee: "b", sec: "sec"; +export { one, bee, sec }; diff --git a/tests/baselines/reference/destructuredDeclarationEmit.symbols b/tests/baselines/reference/destructuredDeclarationEmit.symbols index 2ec953881666a..0f823b46d5621 100644 --- a/tests/baselines/reference/destructuredDeclarationEmit.symbols +++ b/tests/baselines/reference/destructuredDeclarationEmit.symbols @@ -8,15 +8,25 @@ const foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } >bar : Symbol(bar, Decl(foo.ts, 0, 56)) >baz : Symbol(baz, Decl(foo.ts, 0, 66)) -export { foo }; ->foo : Symbol(foo, Decl(foo.ts, 1, 8)) +const arr: [0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]] = [0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]]; +>arr : Symbol(arr, Decl(foo.ts, 1, 5)) +>def : Symbol(def, Decl(foo.ts, 1, 39)) +>sec : Symbol(sec, Decl(foo.ts, 1, 53)) +>def : Symbol(def, Decl(foo.ts, 1, 98)) +>sec : Symbol(sec, Decl(foo.ts, 1, 112)) + +export { foo, arr }; +>foo : Symbol(foo, Decl(foo.ts, 2, 8)) +>arr : Symbol(arr, Decl(foo.ts, 2, 13)) === tests/cases/compiler/index.ts === -import { foo } from './foo'; +import { foo, arr } from './foo'; >foo : Symbol(foo, Decl(index.ts, 0, 8)) +>arr : Symbol(arr, Decl(index.ts, 0, 13)) -export { foo }; +export { foo, arr }; >foo : Symbol(foo, Decl(index.ts, 1, 8)) +>arr : Symbol(arr, Decl(index.ts, 1, 13)) const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; >bar : Symbol(bar, Decl(foo.ts, 0, 13)) @@ -34,3 +44,14 @@ export { baz, ibaz }; >baz : Symbol(baz, Decl(index.ts, 4, 8)) >ibaz : Symbol(ibaz, Decl(index.ts, 4, 13)) +const [ , one, , [, bee, , [, {sec} ]]] = arr; +>one : Symbol(one, Decl(index.ts, 6, 9)) +>bee : Symbol(bee, Decl(index.ts, 6, 19)) +>sec : Symbol(sec, Decl(index.ts, 6, 31)) +>arr : Symbol(arr, Decl(index.ts, 0, 13)) + +export { one, bee, sec }; +>one : Symbol(one, Decl(index.ts, 7, 8)) +>bee : Symbol(bee, Decl(index.ts, 7, 13)) +>sec : Symbol(sec, Decl(index.ts, 7, 18)) + diff --git a/tests/baselines/reference/destructuredDeclarationEmit.types b/tests/baselines/reference/destructuredDeclarationEmit.types index 5f991d39dbf65..b8e28e6bba300 100644 --- a/tests/baselines/reference/destructuredDeclarationEmit.types +++ b/tests/baselines/reference/destructuredDeclarationEmit.types @@ -15,15 +15,38 @@ const foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } >baz : string >'b' : "b" -export { foo }; +const arr: [0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]] = [0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]]; +>arr : [0, 1, 2, ["a", "b", "c", [{ def: "def"; }, { sec: "sec"; }]]] +>def : "def" +>sec : "sec" +>[0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]] : [0, 1, 2, ["a", "b", "c", [{ def: "def"; }, { sec: "sec"; }]]] +>0 : 0 +>1 : 1 +>2 : 2 +>['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]] : ["a", "b", "c", [{ def: "def"; }, { sec: "sec"; }]] +>'a' : "a" +>'b' : "b" +>'c' : "c" +>[{def: 'def'}, {sec: 'sec'}] : [{ def: "def"; }, { sec: "sec"; }] +>{def: 'def'} : { def: "def"; } +>def : string +>'def' : "def" +>{sec: 'sec'} : { sec: "sec"; } +>sec : string +>'sec' : "sec" + +export { foo, arr }; >foo : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } +>arr : [0, 1, 2, ["a", "b", "c", [{ def: "def"; }, { sec: "sec"; }]]] === tests/cases/compiler/index.ts === -import { foo } from './foo'; +import { foo, arr } from './foo'; >foo : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } +>arr : [0, 1, 2, ["a", "b", "c", [{ def: "def"; }, { sec: "sec"; }]]] -export { foo }; +export { foo, arr }; >foo : { bar: string; bat: string; bam: { bork: { bar: string; baz: string; }; }; } +>arr : [0, 1, 2, ["a", "b", "c", [{ def: "def"; }, { sec: "sec"; }]]] const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; >bar : any @@ -41,3 +64,19 @@ export { baz, ibaz }; >baz : string >ibaz : string +const [ , one, , [, bee, , [, {sec} ]]] = arr; +> : undefined +>one : 1 +> : undefined +> : undefined +>bee : "b" +> : undefined +> : undefined +>sec : "sec" +>arr : [0, 1, 2, ["a", "b", "c", [{ def: "def"; }, { sec: "sec"; }]]] + +export { one, bee, sec }; +>one : 1 +>bee : "b" +>sec : "sec" + diff --git a/tests/cases/compiler/destructuredDeclarationEmit.ts b/tests/cases/compiler/destructuredDeclarationEmit.ts index f7f9f67af14ec..73f86f468ea0e 100644 --- a/tests/cases/compiler/destructuredDeclarationEmit.ts +++ b/tests/cases/compiler/destructuredDeclarationEmit.ts @@ -1,10 +1,14 @@ // @declaration: true // @filename: foo.ts const foo = { bar: 'hello', bat: 'world', bam: { bork: { bar: 'a', baz: 'b' } } }; -export { foo }; +const arr: [0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]] = [0, 1, 2, ['a', 'b', 'c', [{def: 'def'}, {sec: 'sec'}]]]; +export { foo, arr }; // @filename: index.ts -import { foo } from './foo'; -export { foo }; +import { foo, arr } from './foo'; +export { foo, arr }; const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; -export { baz, ibaz }; \ No newline at end of file +export { baz, ibaz }; + +const [ , one, , [, bee, , [, {sec} ]]] = arr; +export { one, bee, sec }; \ No newline at end of file From 4c1f9cb38f50278bb3137c28718a771bafbfa29b Mon Sep 17 00:00:00 2001 From: Wesley Wigham <wewigham@microsoft.com> Date: Fri, 17 Nov 2017 14:11:14 -0800 Subject: [PATCH 4/5] Add test case for #18634 --- .../reference/destructuredDeclarationEmit.js | 17 ++++++++++++++- .../destructuredDeclarationEmit.symbols | 16 ++++++++++++++ .../destructuredDeclarationEmit.types | 21 +++++++++++++++++++ .../compiler/destructuredDeclarationEmit.ts | 9 +++++++- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/destructuredDeclarationEmit.js b/tests/baselines/reference/destructuredDeclarationEmit.js index 4ae4cbfc35603..d41976525819e 100644 --- a/tests/baselines/reference/destructuredDeclarationEmit.js +++ b/tests/baselines/reference/destructuredDeclarationEmit.js @@ -12,7 +12,15 @@ const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; export { baz, ibaz }; const [ , one, , [, bee, , [, {sec} ]]] = arr; -export { one, bee, sec }; +export { one, bee, sec }; + +const getFoo = () => ({ + foo: 'foo' +}); + +const { foo: foo2 } = getFoo(); +export { foo2 }; + //// [foo.js] "use strict"; @@ -34,6 +42,11 @@ var one = foo_1.arr[1], _b = foo_1.arr[3], bee = _b[1], _c = _b[3], sec = _c[1]. exports.one = one; exports.bee = bee; exports.sec = sec; +var getFoo = function () { return ({ + foo: 'foo' +}); }; +var foo2 = getFoo().foo; +exports.foo2 = foo2; //// [foo.d.ts] @@ -60,3 +73,5 @@ declare const baz: string, ibaz: string; export { baz, ibaz }; declare const one: 1, bee: "b", sec: "sec"; export { one, bee, sec }; +declare const foo2: string; +export { foo2 }; diff --git a/tests/baselines/reference/destructuredDeclarationEmit.symbols b/tests/baselines/reference/destructuredDeclarationEmit.symbols index 0f823b46d5621..daeed5befd71b 100644 --- a/tests/baselines/reference/destructuredDeclarationEmit.symbols +++ b/tests/baselines/reference/destructuredDeclarationEmit.symbols @@ -55,3 +55,19 @@ export { one, bee, sec }; >bee : Symbol(bee, Decl(index.ts, 7, 13)) >sec : Symbol(sec, Decl(index.ts, 7, 18)) +const getFoo = () => ({ +>getFoo : Symbol(getFoo, Decl(index.ts, 9, 5)) + + foo: 'foo' +>foo : Symbol(foo, Decl(index.ts, 9, 23)) + +}); + +const { foo: foo2 } = getFoo(); +>foo : Symbol(foo, Decl(index.ts, 9, 23)) +>foo2 : Symbol(foo2, Decl(index.ts, 13, 7)) +>getFoo : Symbol(getFoo, Decl(index.ts, 9, 5)) + +export { foo2 }; +>foo2 : Symbol(foo2, Decl(index.ts, 14, 8)) + diff --git a/tests/baselines/reference/destructuredDeclarationEmit.types b/tests/baselines/reference/destructuredDeclarationEmit.types index b8e28e6bba300..94845f94885be 100644 --- a/tests/baselines/reference/destructuredDeclarationEmit.types +++ b/tests/baselines/reference/destructuredDeclarationEmit.types @@ -80,3 +80,24 @@ export { one, bee, sec }; >bee : "b" >sec : "sec" +const getFoo = () => ({ +>getFoo : () => { foo: string; } +>() => ({ foo: 'foo'}) : () => { foo: string; } +>({ foo: 'foo'}) : { foo: string; } +>{ foo: 'foo'} : { foo: string; } + + foo: 'foo' +>foo : string +>'foo' : "foo" + +}); + +const { foo: foo2 } = getFoo(); +>foo : any +>foo2 : string +>getFoo() : { foo: string; } +>getFoo : () => { foo: string; } + +export { foo2 }; +>foo2 : string + diff --git a/tests/cases/compiler/destructuredDeclarationEmit.ts b/tests/cases/compiler/destructuredDeclarationEmit.ts index 73f86f468ea0e..99435aff5b1ad 100644 --- a/tests/cases/compiler/destructuredDeclarationEmit.ts +++ b/tests/cases/compiler/destructuredDeclarationEmit.ts @@ -11,4 +11,11 @@ const { bar: baz, bat, bam: { bork: { bar: ibar, baz: ibaz } } } = foo; export { baz, ibaz }; const [ , one, , [, bee, , [, {sec} ]]] = arr; -export { one, bee, sec }; \ No newline at end of file +export { one, bee, sec }; + +const getFoo = () => ({ + foo: 'foo' +}); + +const { foo: foo2 } = getFoo(); +export { foo2 }; From ea41813731de614f93c6cf0b1af520aee4d2aa8c Mon Sep 17 00:00:00 2001 From: Wesley Wigham <wewigham@microsoft.com> Date: Tue, 21 Nov 2017 14:09:59 -0800 Subject: [PATCH 5/5] Add PR feedback --- src/compiler/checker.ts | 9 +++++++-- src/compiler/declarationEmitter.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 281f0ec922cbc..4a8dce1846e5a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23263,7 +23263,9 @@ namespace ts { function checkExportSpecifier(node: ExportSpecifier) { checkAliasSymbol(node); - collectLinkedAliases(node.propertyName || node.name, /*setVisibility*/ true); // Collect linked aliases to set visibility links + if (compilerOptions.declaration) { + collectLinkedAliases(node.propertyName || node.name, /*setVisibility*/ true); // Collect linked aliases to set visibility links + } if (!(<ExportDeclaration>node.parent.parent).moduleSpecifier) { const exportedName = node.propertyName || node.name; // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) @@ -23301,7 +23303,10 @@ namespace ts { } if (node.expression.kind === SyntaxKind.Identifier) { markExportAsReferenced(node); - collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true); // Sets visibility flags on refernced nodes + + if (compilerOptions.declaration) { + collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true); // Sets visibility flags on refernced nodes + } } else { checkExpressionCached(node.expression); diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index d629f6b450924..dc76e0ddf5028 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1250,7 +1250,7 @@ namespace ts { } function bindingNameContainsVisibleBindingElement(node: BindingName): boolean { - return !!node && isBindingPattern(node) && some(node.elements, elem => !isOmittedExpression(elem) && (resolver.isDeclarationVisible(elem) || bindingNameContainsVisibleBindingElement(elem.name))); + return !!node && isBindingPattern(node) && some(node.elements, elem => !isOmittedExpression(elem) && isVariableDeclarationVisible(elem)); } function isVariableDeclarationVisible(node: VariableDeclaration | BindingElement) {