From 2669ce7c464f95346bf42f1d4510446c260c6935 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 6 Mar 2020 14:42:48 -0800 Subject: [PATCH 1/2] Fix longer type-only property access in non-emitting heritage clauses --- src/compiler/utilities.ts | 14 ++++++++-- tests/baselines/reference/nestedNamespace.js | 28 +++++++++++++++++++ .../reference/nestedNamespace.symbols | 20 +++++++++++++ .../baselines/reference/nestedNamespace.types | 17 +++++++++++ .../typeOnly/nestedNamespace.ts | 8 ++++++ 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/nestedNamespace.js create mode 100644 tests/baselines/reference/nestedNamespace.symbols create mode 100644 tests/baselines/reference/nestedNamespace.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/nestedNamespace.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 36c7765d6f8cf..1668e09c26793 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6292,8 +6292,18 @@ namespace ts { /** Returns true for the first identifier of 1) an `implements` clause, and 2) an `extends` clause of an interface. */ function isFirstIdentifierOfNonEmittingHeritageClause(node: Node): boolean { - // Number of parents to climb from identifier is 2 for `implements I`, 3 for `implements x.I` - const heritageClause = tryCast(node.parent.parent, isHeritageClause) ?? tryCast(node.parent.parent?.parent, isHeritageClause); + if (node.kind !== SyntaxKind.Identifier) return false; + const heritageClause = findAncestor(node.parent, parent => { + switch (parent.kind) { + case SyntaxKind.HeritageClause: + return true; + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ExpressionWithTypeArguments: + return false; + default: + return "quit"; + } + }) as HeritageClause | undefined; return heritageClause?.token === SyntaxKind.ImplementsKeyword || heritageClause?.parent.kind === SyntaxKind.InterfaceDeclaration; } } diff --git a/tests/baselines/reference/nestedNamespace.js b/tests/baselines/reference/nestedNamespace.js new file mode 100644 index 0000000000000..05ee3d7a8abf3 --- /dev/null +++ b/tests/baselines/reference/nestedNamespace.js @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/externalModules/typeOnly/nestedNamespace.ts] //// + +//// [a.ts] +export namespace types { + export class A {} +} + +//// [b.ts] +import type * as a from './a'; +interface B extends a.types.A {} + + +//// [a.js] +"use strict"; +exports.__esModule = true; +exports.types = void 0; +var types; +(function (types) { + var A = /** @class */ (function () { + function A() { + } + return A; + }()); + types.A = A; +})(types = exports.types || (exports.types = {})); +//// [b.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/nestedNamespace.symbols b/tests/baselines/reference/nestedNamespace.symbols new file mode 100644 index 0000000000000..5013b439a42b3 --- /dev/null +++ b/tests/baselines/reference/nestedNamespace.symbols @@ -0,0 +1,20 @@ +=== tests/cases/conformance/externalModules/typeOnly/a.ts === +export namespace types { +>types : Symbol(types, Decl(a.ts, 0, 0)) + + export class A {} +>A : Symbol(A, Decl(a.ts, 0, 24)) +} + +=== tests/cases/conformance/externalModules/typeOnly/b.ts === +import type * as a from './a'; +>a : Symbol(a, Decl(b.ts, 0, 11)) + +interface B extends a.types.A {} +>B : Symbol(B, Decl(b.ts, 0, 30)) +>a.types.A : Symbol(a.types.A, Decl(a.ts, 0, 24)) +>a.types : Symbol(a.types, Decl(a.ts, 0, 0)) +>a : Symbol(a, Decl(b.ts, 0, 11)) +>types : Symbol(a.types, Decl(a.ts, 0, 0)) +>A : Symbol(a.types.A, Decl(a.ts, 0, 24)) + diff --git a/tests/baselines/reference/nestedNamespace.types b/tests/baselines/reference/nestedNamespace.types new file mode 100644 index 0000000000000..21d1fd1d448f5 --- /dev/null +++ b/tests/baselines/reference/nestedNamespace.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/externalModules/typeOnly/a.ts === +export namespace types { +>types : typeof types + + export class A {} +>A : A +} + +=== tests/cases/conformance/externalModules/typeOnly/b.ts === +import type * as a from './a'; +>a : typeof a + +interface B extends a.types.A {} +>a.types : typeof a.types +>a : typeof a +>types : typeof a.types + diff --git a/tests/cases/conformance/externalModules/typeOnly/nestedNamespace.ts b/tests/cases/conformance/externalModules/typeOnly/nestedNamespace.ts new file mode 100644 index 0000000000000..b6a71a112d69b --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/nestedNamespace.ts @@ -0,0 +1,8 @@ +// @Filename: a.ts +export namespace types { + export class A {} +} + +// @Filename: b.ts +import type * as a from './a'; +interface B extends a.types.A {} From eced0e00399e513e7eb129845e14a10fab109df5 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 6 Mar 2020 14:56:56 -0800 Subject: [PATCH 2/2] Rename misnomer function --- src/compiler/utilities.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1668e09c26793..087e047032e5b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6267,7 +6267,7 @@ namespace ts { export function isValidTypeOnlyAliasUseSite(useSite: Node): boolean { return !!(useSite.flags & NodeFlags.Ambient) || isPartOfTypeQuery(useSite) - || isFirstIdentifierOfNonEmittingHeritageClause(useSite) + || isIdentifierInNonEmittingHeritageClause(useSite) || isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) || !isExpressionNode(useSite); } @@ -6290,8 +6290,8 @@ namespace ts { return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral; } - /** Returns true for the first identifier of 1) an `implements` clause, and 2) an `extends` clause of an interface. */ - function isFirstIdentifierOfNonEmittingHeritageClause(node: Node): boolean { + /** Returns true for an identifier in 1) an `implements` clause, and 2) an `extends` clause of an interface. */ + function isIdentifierInNonEmittingHeritageClause(node: Node): boolean { if (node.kind !== SyntaxKind.Identifier) return false; const heritageClause = findAncestor(node.parent, parent => { switch (parent.kind) {