diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a0cac0d375331..167ee1aef2a91 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1792,6 +1792,11 @@ namespace ts { return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral; } + export function isFirstIdentifierOfImplementsClause(node: Node) { + return node.parent?.parent?.parent?.kind === SyntaxKind.HeritageClause + && (node.parent.parent.parent as HeritageClause).token === SyntaxKind.ImplementsKeyword; + } + export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } { return node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference; } @@ -6138,6 +6143,7 @@ namespace ts { export function isValidTypeOnlyAliasUseSite(useSite: Node): boolean { return !!(useSite.flags & NodeFlags.Ambient) || isPartOfTypeQuery(useSite) + || isFirstIdentifierOfImplementsClause(useSite) || isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) || !isExpressionNode(useSite); } diff --git a/tests/baselines/reference/implementsClause.js b/tests/baselines/reference/implementsClause.js new file mode 100644 index 0000000000000..431c9042f5b67 --- /dev/null +++ b/tests/baselines/reference/implementsClause.js @@ -0,0 +1,36 @@ +//// [tests/cases/conformance/externalModules/typeOnly/implementsClause.ts] //// + +//// [types.ts] +export interface Component {} + +//// [ns.ts] +import type * as types from './types'; +export { types }; + +//// [index.ts] +import type * as types from './types'; +import * as nestedNamespace from './ns'; + +class C implements types.Component {} +class D implements nestedNamespace.types.Component {} + + +//// [types.js] +"use strict"; +exports.__esModule = true; +//// [ns.js] +"use strict"; +exports.__esModule = true; +//// [index.js] +"use strict"; +exports.__esModule = true; +var C = /** @class */ (function () { + function C() { + } + return C; +}()); +var D = /** @class */ (function () { + function D() { + } + return D; +}()); diff --git a/tests/baselines/reference/implementsClause.symbols b/tests/baselines/reference/implementsClause.symbols new file mode 100644 index 0000000000000..b865e9187cb19 --- /dev/null +++ b/tests/baselines/reference/implementsClause.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/externalModules/typeOnly/types.ts === +export interface Component {} +>Component : Symbol(Component, Decl(types.ts, 0, 0)) + +=== tests/cases/conformance/externalModules/typeOnly/ns.ts === +import type * as types from './types'; +>types : Symbol(types, Decl(ns.ts, 0, 11)) + +export { types }; +>types : Symbol(types, Decl(ns.ts, 1, 8)) + +=== tests/cases/conformance/externalModules/typeOnly/index.ts === +import type * as types from './types'; +>types : Symbol(types, Decl(index.ts, 0, 11)) + +import * as nestedNamespace from './ns'; +>nestedNamespace : Symbol(nestedNamespace, Decl(index.ts, 1, 6)) + +class C implements types.Component {} +>C : Symbol(C, Decl(index.ts, 1, 40)) +>types.Component : Symbol(types.Component, Decl(types.ts, 0, 0)) +>types : Symbol(types, Decl(index.ts, 0, 11)) +>Component : Symbol(types.Component, Decl(types.ts, 0, 0)) + +class D implements nestedNamespace.types.Component {} +>D : Symbol(D, Decl(index.ts, 3, 37)) +>nestedNamespace.types.Component : Symbol(types.Component, Decl(types.ts, 0, 0)) +>nestedNamespace.types : Symbol(nestedNamespace.types, Decl(ns.ts, 1, 8)) +>nestedNamespace : Symbol(nestedNamespace, Decl(index.ts, 1, 6)) +>types : Symbol(nestedNamespace.types, Decl(ns.ts, 1, 8)) +>Component : Symbol(types.Component, Decl(types.ts, 0, 0)) + diff --git a/tests/baselines/reference/implementsClause.types b/tests/baselines/reference/implementsClause.types new file mode 100644 index 0000000000000..ffdff2f6fb08d --- /dev/null +++ b/tests/baselines/reference/implementsClause.types @@ -0,0 +1,27 @@ +=== tests/cases/conformance/externalModules/typeOnly/types.ts === +export interface Component {} +No type information for this code. +No type information for this code.=== tests/cases/conformance/externalModules/typeOnly/ns.ts === +import type * as types from './types'; +>types : typeof types + +export { types }; +>types : typeof types + +=== tests/cases/conformance/externalModules/typeOnly/index.ts === +import type * as types from './types'; +>types : typeof types + +import * as nestedNamespace from './ns'; +>nestedNamespace : typeof nestedNamespace + +class C implements types.Component {} +>C : C +>types : typeof types + +class D implements nestedNamespace.types.Component {} +>D : D +>nestedNamespace.types : any +>nestedNamespace : typeof nestedNamespace +>types : any + diff --git a/tests/cases/conformance/externalModules/typeOnly/implementsClause.ts b/tests/cases/conformance/externalModules/typeOnly/implementsClause.ts new file mode 100644 index 0000000000000..71926627d2470 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/implementsClause.ts @@ -0,0 +1,13 @@ +// @Filename: types.ts +export interface Component {} + +// @Filename: ns.ts +import type * as types from './types'; +export { types }; + +// @Filename: index.ts +import type * as types from './types'; +import * as nestedNamespace from './ns'; + +class C implements types.Component {} +class D implements nestedNamespace.types.Component {}