From 61bdfa9697f638e0eb94ca57ec4204139b9a0dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 28 Apr 2022 09:21:41 +0200 Subject: [PATCH 1/2] Add visible alias statements for non-visible binding patterns when emitting declaration --- src/compiler/checker.ts | 26 ++++++--- ...arationEmitExpressionInExtends6.errors.txt | 15 ----- ...eclarationEmitNonExportedBindingPattern.js | 48 ++++++++++++++++ ...ationEmitNonExportedBindingPattern.symbols | 45 +++++++++++++++ ...arationEmitNonExportedBindingPattern.types | 55 +++++++++++++++++++ ...eclarationEmitNonExportedBindingPattern.ts | 22 ++++++++ 6 files changed, 189 insertions(+), 22 deletions(-) delete mode 100644 tests/baselines/reference/declarationEmitExpressionInExtends6.errors.txt create mode 100644 tests/baselines/reference/declarationEmitNonExportedBindingPattern.js create mode 100644 tests/baselines/reference/declarationEmitNonExportedBindingPattern.symbols create mode 100644 tests/baselines/reference/declarationEmitNonExportedBindingPattern.types create mode 100644 tests/cases/compiler/declarationEmitNonExportedBindingPattern.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1506c4809ef33..acd4264ddc814 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4607,13 +4607,25 @@ namespace ts { && isDeclarationVisible(declaration.parent)) { return addVisibleAlias(declaration, declaration); } - else if (symbol.flags & SymbolFlags.Alias && isBindingElement(declaration) && isInJSFile(declaration) && declaration.parent?.parent // exported import-like top-level JS require statement - && isVariableDeclaration(declaration.parent.parent) - && declaration.parent.parent.parent?.parent && isVariableStatement(declaration.parent.parent.parent.parent) - && !hasSyntacticModifier(declaration.parent.parent.parent.parent, ModifierFlags.Export) - && declaration.parent.parent.parent.parent.parent // check if the thing containing the variable statement is visible (ie, the file) - && isDeclarationVisible(declaration.parent.parent.parent.parent.parent)) { - return addVisibleAlias(declaration, declaration.parent.parent.parent.parent); + else if (isBindingElement(declaration)) { + if (symbol.flags & SymbolFlags.Alias && isInJSFile(declaration) && declaration.parent?.parent // exported import-like top-level JS require statement + && isVariableDeclaration(declaration.parent.parent) + && declaration.parent.parent.parent?.parent && isVariableStatement(declaration.parent.parent.parent.parent) + && !hasSyntacticModifier(declaration.parent.parent.parent.parent, ModifierFlags.Export) + && declaration.parent.parent.parent.parent.parent // check if the thing containing the variable statement is visible (ie, the file) + && isDeclarationVisible(declaration.parent.parent.parent.parent.parent)) { + return addVisibleAlias(declaration, declaration.parent.parent.parent.parent); + } + else if (symbol.flags & SymbolFlags.BlockScopedVariable) { + const variableStatement = findAncestor(declaration, isVariableStatement)!; + if (hasSyntacticModifier(variableStatement, ModifierFlags.Export)) { + return true; + } + if (!isDeclarationVisible(variableStatement.parent)) { + return false; + } + return addVisibleAlias(declaration, variableStatement); + } } // Declaration is not visible diff --git a/tests/baselines/reference/declarationEmitExpressionInExtends6.errors.txt b/tests/baselines/reference/declarationEmitExpressionInExtends6.errors.txt deleted file mode 100644 index acd1e1a7c9eeb..0000000000000 --- a/tests/baselines/reference/declarationEmitExpressionInExtends6.errors.txt +++ /dev/null @@ -1,15 +0,0 @@ -/b.ts(2,30): error TS4021: 'extends' clause of exported class has or is using private name 'Foo'. - - -==== /b.ts (1 errors) ==== - const { Foo } = require("./a"); - export default class extends Foo {} - ~~~ -!!! error TS4021: 'extends' clause of exported class has or is using private name 'Foo'. - -==== /node_modules/@types/node/index.d.ts (0 errors) ==== - declare const require: any; - -==== /a.js (0 errors) ==== - export class Foo {} - \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmitNonExportedBindingPattern.js b/tests/baselines/reference/declarationEmitNonExportedBindingPattern.js new file mode 100644 index 0000000000000..2bc10512f829e --- /dev/null +++ b/tests/baselines/reference/declarationEmitNonExportedBindingPattern.js @@ -0,0 +1,48 @@ +//// [test.ts] +function getFoo() { + return { foo: { test: 42 } } +} + +const { foo } = getFoo() + +export type AliasType = typeof foo + +const { foo: renamed } = getFoo() + +export type AliasType2 = typeof renamed + +function getNested() { + return { a: { b: { c: 'd' } } } +} + +const { a: { b: { c } } } = getNested() + +export type AliasType3 = typeof c + + +//// [test.js] +"use strict"; +exports.__esModule = true; +function getFoo() { + return { foo: { test: 42 } }; +} +var foo = getFoo().foo; +var renamed = getFoo().foo; +function getNested() { + return { a: { b: { c: 'd' } } }; +} +var c = getNested().a.b.c; + + +//// [test.d.ts] +declare const foo: { + test: number; +}; +export declare type AliasType = typeof foo; +declare const renamed: { + test: number; +}; +export declare type AliasType2 = typeof renamed; +declare const c: string; +export declare type AliasType3 = typeof c; +export {}; diff --git a/tests/baselines/reference/declarationEmitNonExportedBindingPattern.symbols b/tests/baselines/reference/declarationEmitNonExportedBindingPattern.symbols new file mode 100644 index 0000000000000..e87423045c8d2 --- /dev/null +++ b/tests/baselines/reference/declarationEmitNonExportedBindingPattern.symbols @@ -0,0 +1,45 @@ +=== tests/cases/compiler/test.ts === +function getFoo() { +>getFoo : Symbol(getFoo, Decl(test.ts, 0, 0)) + + return { foo: { test: 42 } } +>foo : Symbol(foo, Decl(test.ts, 1, 10)) +>test : Symbol(test, Decl(test.ts, 1, 17)) +} + +const { foo } = getFoo() +>foo : Symbol(foo, Decl(test.ts, 4, 7)) +>getFoo : Symbol(getFoo, Decl(test.ts, 0, 0)) + +export type AliasType = typeof foo +>AliasType : Symbol(AliasType, Decl(test.ts, 4, 24)) +>foo : Symbol(foo, Decl(test.ts, 4, 7)) + +const { foo: renamed } = getFoo() +>foo : Symbol(foo, Decl(test.ts, 1, 10)) +>renamed : Symbol(renamed, Decl(test.ts, 8, 7)) +>getFoo : Symbol(getFoo, Decl(test.ts, 0, 0)) + +export type AliasType2 = typeof renamed +>AliasType2 : Symbol(AliasType2, Decl(test.ts, 8, 33)) +>renamed : Symbol(renamed, Decl(test.ts, 8, 7)) + +function getNested() { +>getNested : Symbol(getNested, Decl(test.ts, 10, 39)) + + return { a: { b: { c: 'd' } } } +>a : Symbol(a, Decl(test.ts, 13, 10)) +>b : Symbol(b, Decl(test.ts, 13, 15)) +>c : Symbol(c, Decl(test.ts, 13, 20)) +} + +const { a: { b: { c } } } = getNested() +>a : Symbol(a, Decl(test.ts, 13, 10)) +>b : Symbol(b, Decl(test.ts, 13, 15)) +>c : Symbol(c, Decl(test.ts, 16, 17)) +>getNested : Symbol(getNested, Decl(test.ts, 10, 39)) + +export type AliasType3 = typeof c +>AliasType3 : Symbol(AliasType3, Decl(test.ts, 16, 39)) +>c : Symbol(c, Decl(test.ts, 16, 17)) + diff --git a/tests/baselines/reference/declarationEmitNonExportedBindingPattern.types b/tests/baselines/reference/declarationEmitNonExportedBindingPattern.types new file mode 100644 index 0000000000000..518a991a6eef8 --- /dev/null +++ b/tests/baselines/reference/declarationEmitNonExportedBindingPattern.types @@ -0,0 +1,55 @@ +=== tests/cases/compiler/test.ts === +function getFoo() { +>getFoo : () => { foo: { test: number; }; } + + return { foo: { test: 42 } } +>{ foo: { test: 42 } } : { foo: { test: number; }; } +>foo : { test: number; } +>{ test: 42 } : { test: number; } +>test : number +>42 : 42 +} + +const { foo } = getFoo() +>foo : { test: number; } +>getFoo() : { foo: { test: number; }; } +>getFoo : () => { foo: { test: number; }; } + +export type AliasType = typeof foo +>AliasType : { test: number; } +>foo : { test: number; } + +const { foo: renamed } = getFoo() +>foo : any +>renamed : { test: number; } +>getFoo() : { foo: { test: number; }; } +>getFoo : () => { foo: { test: number; }; } + +export type AliasType2 = typeof renamed +>AliasType2 : { test: number; } +>renamed : { test: number; } + +function getNested() { +>getNested : () => { a: { b: { c: string; }; }; } + + return { a: { b: { c: 'd' } } } +>{ a: { b: { c: 'd' } } } : { a: { b: { c: string; }; }; } +>a : { b: { c: string; }; } +>{ b: { c: 'd' } } : { b: { c: string; }; } +>b : { c: string; } +>{ c: 'd' } : { c: string; } +>c : string +>'d' : "d" +} + +const { a: { b: { c } } } = getNested() +>a : any +>b : any +>c : string +>getNested() : { a: { b: { c: string; }; }; } +>getNested : () => { a: { b: { c: string; }; }; } + +export type AliasType3 = typeof c +>AliasType3 : string +>c : string + diff --git a/tests/cases/compiler/declarationEmitNonExportedBindingPattern.ts b/tests/cases/compiler/declarationEmitNonExportedBindingPattern.ts new file mode 100644 index 0000000000000..a01eafc2a5f6f --- /dev/null +++ b/tests/cases/compiler/declarationEmitNonExportedBindingPattern.ts @@ -0,0 +1,22 @@ +// @declaration: true +// @filename: test.ts + +function getFoo() { + return { foo: { test: 42 } } +} + +const { foo } = getFoo() + +export type AliasType = typeof foo + +const { foo: renamed } = getFoo() + +export type AliasType2 = typeof renamed + +function getNested() { + return { a: { b: { c: 'd' } } } +} + +const { a: { b: { c } } } = getNested() + +export type AliasType3 = typeof c From 6ce769743f72f4b248ab5f37753a1c242bee422a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 20 Jun 2022 09:19:23 +0200 Subject: [PATCH 2/2] Rewrite `declarationEmitExpressionInExtends6` test to make it pass after the changes --- .../declarationEmitExpressionInExtends6.js | 29 +++++++++++++++++-- ...eclarationEmitExpressionInExtends6.symbols | 23 ++++++++++----- .../declarationEmitExpressionInExtends6.types | 25 +++++++++------- .../declarationEmitExpressionInExtends6.ts | 5 ++-- 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/tests/baselines/reference/declarationEmitExpressionInExtends6.js b/tests/baselines/reference/declarationEmitExpressionInExtends6.js index 3e6f13b388728..c4e9df05e21af 100644 --- a/tests/baselines/reference/declarationEmitExpressionInExtends6.js +++ b/tests/baselines/reference/declarationEmitExpressionInExtends6.js @@ -3,14 +3,25 @@ //// [index.d.ts] declare const require: any; -//// [a.js] +//// [a.ts] export class Foo {} //// [b.ts] -const { Foo } = require("./a"); +import * as A from "./a"; +const { Foo } = A; export default class extends Foo {} +//// [a.js] +"use strict"; +exports.__esModule = true; +exports.Foo = void 0; +var Foo = /** @class */ (function () { + function Foo() { + } + return Foo; +}()); +exports.Foo = Foo; //// [b.js] "use strict"; var __extends = (this && this.__extends) || (function () { @@ -29,7 +40,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); exports.__esModule = true; -var Foo = require("./a").Foo; +var A = require("./a"); +var Foo = A.Foo; var default_1 = /** @class */ (function (_super) { __extends(default_1, _super); function default_1() { @@ -38,3 +50,14 @@ var default_1 = /** @class */ (function (_super) { return default_1; }(Foo)); exports["default"] = default_1; + + +//// [a.d.ts] +export declare class Foo { +} +//// [b.d.ts] +import * as A from "./a"; +declare const Foo: typeof A.Foo; +export default class extends Foo { +} +export {}; diff --git a/tests/baselines/reference/declarationEmitExpressionInExtends6.symbols b/tests/baselines/reference/declarationEmitExpressionInExtends6.symbols index 32eaa9018c228..0a7c2a159bdbe 100644 --- a/tests/baselines/reference/declarationEmitExpressionInExtends6.symbols +++ b/tests/baselines/reference/declarationEmitExpressionInExtends6.symbols @@ -1,12 +1,19 @@ -=== /b.ts === -const { Foo } = require("./a"); ->Foo : Symbol(Foo, Decl(b.ts, 0, 7)) ->require : Symbol(require, Decl(index.d.ts, 0, 13)) - -export default class extends Foo {} ->Foo : Symbol(Foo, Decl(b.ts, 0, 7)) - === /node_modules/@types/node/index.d.ts === declare const require: any; >require : Symbol(require, Decl(index.d.ts, 0, 13)) +=== /a.ts === +export class Foo {} +>Foo : Symbol(Foo, Decl(a.ts, 0, 0)) + +=== /b.ts === +import * as A from "./a"; +>A : Symbol(A, Decl(b.ts, 0, 6)) + +const { Foo } = A; +>Foo : Symbol(Foo, Decl(b.ts, 1, 7)) +>A : Symbol(A, Decl(b.ts, 0, 6)) + +export default class extends Foo {} +>Foo : Symbol(Foo, Decl(b.ts, 1, 7)) + diff --git a/tests/baselines/reference/declarationEmitExpressionInExtends6.types b/tests/baselines/reference/declarationEmitExpressionInExtends6.types index 03ec99ecc4f34..520350cf4e1dc 100644 --- a/tests/baselines/reference/declarationEmitExpressionInExtends6.types +++ b/tests/baselines/reference/declarationEmitExpressionInExtends6.types @@ -1,14 +1,19 @@ -=== /b.ts === -const { Foo } = require("./a"); ->Foo : any ->require("./a") : any ->require : any ->"./a" : "./a" - -export default class extends Foo {} ->Foo : any - === /node_modules/@types/node/index.d.ts === declare const require: any; >require : any +=== /a.ts === +export class Foo {} +>Foo : Foo + +=== /b.ts === +import * as A from "./a"; +>A : typeof A + +const { Foo } = A; +>Foo : typeof A.Foo +>A : typeof A + +export default class extends Foo {} +>Foo : A.Foo + diff --git a/tests/cases/compiler/declarationEmitExpressionInExtends6.ts b/tests/cases/compiler/declarationEmitExpressionInExtends6.ts index 876be6ded3c9b..15069c6fbff22 100644 --- a/tests/cases/compiler/declarationEmitExpressionInExtends6.ts +++ b/tests/cases/compiler/declarationEmitExpressionInExtends6.ts @@ -7,9 +7,10 @@ // @Filename: /node_modules/@types/node/index.d.ts declare const require: any; -// @Filename: /a.js +// @Filename: /a.ts export class Foo {} // @Filename: /b.ts -const { Foo } = require("./a"); +import * as A from "./a"; +const { Foo } = A; export default class extends Foo {}