From 7e1382aa1abb3cd14846152ea8249d7f645d97a9 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 28 Jan 2020 11:33:42 -0800 Subject: [PATCH] Fix interface extends clause --- src/compiler/utilities.ts | 9 +-- .../reference/extendsClause.errors.txt | 30 +++++++++ tests/baselines/reference/extendsClause.js | 65 +++++++++++++++++++ .../baselines/reference/extendsClause.symbols | 52 +++++++++++++++ tests/baselines/reference/extendsClause.types | 38 +++++++++++ .../externalModules/typeOnly/extendsClause.ts | 19 ++++++ 6 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/extendsClause.errors.txt create mode 100644 tests/baselines/reference/extendsClause.js create mode 100644 tests/baselines/reference/extendsClause.symbols create mode 100644 tests/baselines/reference/extendsClause.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/extendsClause.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 167ee1aef2a..863076f6783 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1792,9 +1792,10 @@ 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 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); + return heritageClause?.token === SyntaxKind.ImplementsKeyword || heritageClause?.parent.kind === SyntaxKind.InterfaceDeclaration; } export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } { @@ -6143,7 +6144,7 @@ namespace ts { export function isValidTypeOnlyAliasUseSite(useSite: Node): boolean { return !!(useSite.flags & NodeFlags.Ambient) || isPartOfTypeQuery(useSite) - || isFirstIdentifierOfImplementsClause(useSite) + || isFirstIdentifierOfNonEmittingHeritageClause(useSite) || isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) || !isExpressionNode(useSite); } diff --git a/tests/baselines/reference/extendsClause.errors.txt b/tests/baselines/reference/extendsClause.errors.txt new file mode 100644 index 00000000000..7ee2b0600c3 --- /dev/null +++ b/tests/baselines/reference/extendsClause.errors.txt @@ -0,0 +1,30 @@ +tests/cases/conformance/externalModules/typeOnly/index.ts(9,17): error TS1361: 'C' cannot be used as a value because it was imported using 'import type'. +tests/cases/conformance/externalModules/typeOnly/index.ts(10,17): error TS1361: 'types' cannot be used as a value because it was imported using 'import type'. + + +==== tests/cases/conformance/externalModules/typeOnly/types.ts (0 errors) ==== + export interface I {} + export class C {} + +==== tests/cases/conformance/externalModules/typeOnly/ns.ts (0 errors) ==== + import type * as types from './types'; + export { types }; + +==== tests/cases/conformance/externalModules/typeOnly/index.ts (2 errors) ==== + import { types } from './ns'; + import type { C, I } from './types'; + + interface Q extends C {} + interface R extends I {} + interface S extends types.C {} + interface T extends types.I {} + + class U extends C {} // Error + ~ +!!! error TS1361: 'C' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 tests/cases/conformance/externalModules/typeOnly/index.ts:2:15: 'C' was imported here. + class V extends types.C {} // Error + ~~~~~ +!!! error TS1361: 'types' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 tests/cases/conformance/externalModules/typeOnly/ns.ts:1:13: 'types' was imported here. + \ No newline at end of file diff --git a/tests/baselines/reference/extendsClause.js b/tests/baselines/reference/extendsClause.js new file mode 100644 index 00000000000..d4b31c04840 --- /dev/null +++ b/tests/baselines/reference/extendsClause.js @@ -0,0 +1,65 @@ +//// [tests/cases/conformance/externalModules/typeOnly/extendsClause.ts] //// + +//// [types.ts] +export interface I {} +export class C {} + +//// [ns.ts] +import type * as types from './types'; +export { types }; + +//// [index.ts] +import { types } from './ns'; +import type { C, I } from './types'; + +interface Q extends C {} +interface R extends I {} +interface S extends types.C {} +interface T extends types.I {} + +class U extends C {} // Error +class V extends types.C {} // Error + + +//// [types.js] +"use strict"; +exports.__esModule = true; +var C = /** @class */ (function () { + function C() { + } + return C; +}()); +exports.C = C; +//// [ns.js] +"use strict"; +exports.__esModule = true; +//// [index.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var U = /** @class */ (function (_super) { + __extends(U, _super); + function U() { + return _super !== null && _super.apply(this, arguments) || this; + } + return U; +}(C)); // Error +var V = /** @class */ (function (_super) { + __extends(V, _super); + function V() { + return _super !== null && _super.apply(this, arguments) || this; + } + return V; +}(types.C)); // Error diff --git a/tests/baselines/reference/extendsClause.symbols b/tests/baselines/reference/extendsClause.symbols new file mode 100644 index 00000000000..9a9e53d4deb --- /dev/null +++ b/tests/baselines/reference/extendsClause.symbols @@ -0,0 +1,52 @@ +=== tests/cases/conformance/externalModules/typeOnly/types.ts === +export interface I {} +>I : Symbol(I, Decl(types.ts, 0, 0)) + +export class C {} +>C : Symbol(C, Decl(types.ts, 0, 21)) + +=== 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 { types } from './ns'; +>types : Symbol(types, Decl(index.ts, 0, 8)) + +import type { C, I } from './types'; +>C : Symbol(C, Decl(index.ts, 1, 13)) +>I : Symbol(I, Decl(index.ts, 1, 16)) + +interface Q extends C {} +>Q : Symbol(Q, Decl(index.ts, 1, 36)) +>C : Symbol(C, Decl(index.ts, 1, 13)) + +interface R extends I {} +>R : Symbol(R, Decl(index.ts, 3, 24)) +>I : Symbol(I, Decl(index.ts, 1, 16)) + +interface S extends types.C {} +>S : Symbol(S, Decl(index.ts, 4, 24)) +>types.C : Symbol(types.C, Decl(types.ts, 0, 21)) +>types : Symbol(types, Decl(index.ts, 0, 8)) +>C : Symbol(types.C, Decl(types.ts, 0, 21)) + +interface T extends types.I {} +>T : Symbol(T, Decl(index.ts, 5, 30)) +>types.I : Symbol(types.I, Decl(types.ts, 0, 0)) +>types : Symbol(types, Decl(index.ts, 0, 8)) +>I : Symbol(types.I, Decl(types.ts, 0, 0)) + +class U extends C {} // Error +>U : Symbol(U, Decl(index.ts, 6, 30)) +>C : Symbol(C, Decl(index.ts, 1, 13)) + +class V extends types.C {} // Error +>V : Symbol(V, Decl(index.ts, 8, 20)) +>types.C : Symbol(types.C, Decl(types.ts, 0, 21)) +>types : Symbol(types, Decl(index.ts, 0, 8)) +>C : Symbol(types.C, Decl(types.ts, 0, 21)) + diff --git a/tests/baselines/reference/extendsClause.types b/tests/baselines/reference/extendsClause.types new file mode 100644 index 00000000000..15140fd0fd6 --- /dev/null +++ b/tests/baselines/reference/extendsClause.types @@ -0,0 +1,38 @@ +=== tests/cases/conformance/externalModules/typeOnly/types.ts === +export interface I {} +export class C {} +>C : C + +=== 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 { types } from './ns'; +>types : typeof types + +import type { C, I } from './types'; +>C : types.C +>I : types.I + +interface Q extends C {} +interface R extends I {} +interface S extends types.C {} +>types : typeof types + +interface T extends types.I {} +>types : typeof types + +class U extends C {} // Error +>U : U +>C : types.C + +class V extends types.C {} // Error +>V : V +>types.C : types.C +>types : typeof types +>C : typeof types.C + diff --git a/tests/cases/conformance/externalModules/typeOnly/extendsClause.ts b/tests/cases/conformance/externalModules/typeOnly/extendsClause.ts new file mode 100644 index 00000000000..0288d07a8f8 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/extendsClause.ts @@ -0,0 +1,19 @@ +// @Filename: types.ts +export interface I {} +export class C {} + +// @Filename: ns.ts +import type * as types from './types'; +export { types }; + +// @Filename: index.ts +import { types } from './ns'; +import type { C, I } from './types'; + +interface Q extends C {} +interface R extends I {} +interface S extends types.C {} +interface T extends types.I {} + +class U extends C {} // Error +class V extends types.C {} // Error