From b40284c811d7e2664918c17c23f6bc7893f7df53 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 8 Jan 2020 13:27:07 -0800 Subject: [PATCH 01/24] Change type-only semantics to allow type queries --- src/compiler/checker.ts | 187 ++++++------------ src/compiler/diagnosticMessages.json | 14 +- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 8 - src/compiler/utilitiesPublic.ts | 14 +- src/services/symbolDisplay.ts | 9 - tests/baselines/reference/chained.errors.txt | 5 +- tests/baselines/reference/chained.symbols | 4 +- tests/baselines/reference/chained.types | 20 +- tests/baselines/reference/enums.errors.txt | 10 +- tests/baselines/reference/enums.js | 2 +- tests/baselines/reference/enums.symbols | 12 +- tests/baselines/reference/enums.types | 20 +- .../reference/exportDeclaration.errors.txt | 5 +- .../reference/exportDeclaration.symbols | 1 + .../reference/exportDeclaration.types | 6 +- ...portDeclaration_moduleSpecifier.errors.txt | 5 +- .../exportDeclaration_moduleSpecifier.symbols | 1 + .../exportDeclaration_moduleSpecifier.types | 8 +- .../exportDeclaration_value.errors.txt | 18 -- .../filterNamespace_import.errors.txt | 30 --- .../reference/filterNamespace_import.js | 4 +- .../reference/filterNamespace_import.symbols | 12 +- .../reference/filterNamespace_import.types | 16 +- tests/baselines/reference/generic.symbols | 2 +- tests/baselines/reference/generic.types | 12 +- .../reference/importClause_default.errors.txt | 13 -- .../reference/importClause_default.js | 2 +- .../reference/importClause_default.symbols | 2 + .../reference/importClause_default.types | 8 +- .../importClause_namedImports.errors.txt | 11 +- .../importClause_namedImports.symbols | 2 + .../reference/importClause_namedImports.types | 8 +- .../importClause_namespaceImport.errors.txt | 8 +- .../importClause_namespaceImport.symbols | 10 +- .../importClause_namespaceImport.types | 14 +- tests/baselines/reference/renamed.symbols | 2 +- tests/baselines/reference/renamed.types | 12 +- tests/baselines/reference/typeQuery.js | 23 +++ tests/baselines/reference/typeQuery.symbols | 12 ++ tests/baselines/reference/typeQuery.types | 12 ++ .../externalModules/typeOnly/typeQuery.ts | 6 + 42 files changed, 259 insertions(+), 312 deletions(-) delete mode 100644 tests/baselines/reference/exportDeclaration_value.errors.txt delete mode 100644 tests/baselines/reference/filterNamespace_import.errors.txt delete mode 100644 tests/baselines/reference/importClause_default.errors.txt create mode 100644 tests/baselines/reference/typeQuery.js create mode 100644 tests/baselines/reference/typeQuery.symbols create mode 100644 tests/baselines/reference/typeQuery.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/typeQuery.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5fd321dc90a48..3ebe44d35fa5e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1851,6 +1851,21 @@ namespace ts { error(errorLocation, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializer.name), declarationNameToString(errorLocation)); } } + if (result && errorLocation && meaning & SymbolFlags.Value && isInExpressionContext(errorLocation)) { + const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result); + if (typeOnlyDeclaration) { + const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier + ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type + : Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type; + const relatedMessage = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier + ? Diagnostics._0_was_exported_here + : Diagnostics._0_was_imported_here; + const unescapedName = unescapeLeadingUnderscores(name); + addRelatedInfo( + error(errorLocation, message, unescapedName), + createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, unescapedName)); + } + } } return result; } @@ -2045,9 +2060,7 @@ namespace ts { if (symbol) { error( errorLocation, - isTypeOnlyEnumAlias(symbol) - ? Diagnostics.Enum_0_cannot_be_used_as_a_value_because_only_its_type_has_been_imported - : Diagnostics.Cannot_use_namespace_0_as_a_value, + Diagnostics.Cannot_use_namespace_0_as_a_value, unescapeLeadingUnderscores(name)); return true; } @@ -2222,25 +2235,15 @@ namespace ts { } else if (hasSyntheticDefault) { // per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present - return maybeTypeOnly( - resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || - resolveSymbol(moduleSymbol, dontResolveAlias)); + return resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); } - return maybeTypeOnly(exportDefaultSymbol); - } - - function maybeTypeOnly(symbol: Symbol | undefined) { - if (symbol && node.isTypeOnly && node.name) { - return createTypeOnlyImportOrExport(node.name, symbol); - } - return symbol; + return exportDefaultSymbol; } } function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol | undefined { const moduleSpecifier = node.parent.parent.moduleSpecifier; - const moduleSymbol = resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false); - return moduleSymbol && node.parent.isTypeOnly ? createTypeOnlySymbol(moduleSymbol) : moduleSymbol; + return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false); } function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined { @@ -2359,98 +2362,17 @@ namespace ts { } function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol | undefined { - const resolved = getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias); - if (resolved && node.parent.parent.isTypeOnly) { - return createTypeOnlyImportOrExport(node.name, resolved); - } - return resolved; + return getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias); } function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol { return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias); } - /** - * Creates a type alias symbol with a target symbol for type-only imports and exports. - * The symbol for `A` in `export type { A }` or `export type { A } from "./mod"` has - * `TypeFlags.Alias` so that alias resolution works as usual, but once the target `A` - * has been resolved, we essentially want to pretend we have a type alias to that target. - */ - function createTypeOnlyImportOrExport(sourceNode: ExportSpecifier | Identifier, target: Symbol) { - const symbol = createTypeOnlySymbol(target); - if (!symbol && target !== unknownSymbol) { - const identifier = isExportSpecifier(sourceNode) ? sourceNode.name : sourceNode; - const nameText = idText(identifier); - const diagnostic = error( - identifier, - Diagnostics.Type_only_0_must_reference_a_type_but_1_is_a_value, - isExportSpecifier(sourceNode) ? "export" : "import", - nameText); - const targetDeclaration = target.valueDeclaration ?? target.declarations?.[0]; - if (targetDeclaration) { - addRelatedInfo(diagnostic, createDiagnosticForNode( - targetDeclaration, - Diagnostics._0_is_declared_here, - nameText)); - } - } - - return symbol; - } - - function createTypeOnlySymbol(target: Symbol): Symbol | undefined { - if (target.flags & SymbolFlags.ValueModule) { - return createNamespaceModuleForModule(target); - } - if (target.flags & SymbolFlags.Enum) { - return createNamespaceModuleForEnum(target); - } - if (!(target.flags & SymbolFlags.Value)) { - return target; - } - if (target.flags & SymbolFlags.Type) { - const alias = createSymbol(SymbolFlags.TypeAlias, target.escapedName); - alias.declarations = emptyArray; - alias.immediateTarget = target; - return alias; - } - } - - function createNamespaceModuleForEnum(enumSymbol: Symbol) { - Debug.assert(!!(enumSymbol.flags & SymbolFlags.Enum)); - const symbol = createSymbol(SymbolFlags.NamespaceModule | SymbolFlags.TypeAlias, enumSymbol.escapedName); - symbol.immediateTarget = enumSymbol; - symbol.declarations = enumSymbol.declarations; - if (enumSymbol.exports) { - symbol.exports = createSymbolTable(); - enumSymbol.exports.forEach((exportSymbol, key) => { - symbol.exports!.set(key, Debug.assertDefined(createTypeOnlySymbol(exportSymbol))); - }); - } - return symbol; - } - - function createNamespaceModuleForModule(moduleSymbol: Symbol) { - Debug.assert(!!(moduleSymbol.flags & SymbolFlags.ValueModule)); - const filtered = createSymbol(SymbolFlags.NamespaceModule, moduleSymbol.escapedName); - filtered.declarations = moduleSymbol.declarations; - if (moduleSymbol.exports) { - filtered.exports = createSymbolTable(); - moduleSymbol.exports.forEach((exportSymbol, key) => { - const typeOnlyExport = createTypeOnlySymbol(exportSymbol); - if (typeOnlyExport) { - filtered.exports!.set(key, typeOnlyExport); - } - }); - } - return filtered; - } - function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) { - const target = node.parent.parent.moduleSpecifier ? + return node.parent.parent.moduleSpecifier ? getExternalModuleMember(node.parent.parent, node, dontResolveAlias) : resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias); - return target && node.parent.parent.isTypeOnly ? createTypeOnlyImportOrExport(node, target) : target; } function getTargetOfExportAssignment(node: ExportAssignment | BinaryExpression, dontResolveAlias: boolean): Symbol | undefined { @@ -2552,12 +2474,34 @@ namespace ts { return links.target; } + /** Indicates that a symbol directly or indirectly resolves to a type-only import or export. */ + function getTypeOnlyAliasDeclaration(symbol: Symbol): Identifier | ImportSpecifier | ExportSpecifier | undefined { + const links = getSymbolLinks(symbol); + if (links.typeOnlyDeclaration === undefined) { + if (symbol.flags & SymbolFlags.Alias) { + const node = getDeclarationOfAliasSymbol(symbol); + if (!node) return Debug.fail(); + if (isTypeOnlyImportOrExportName(node)) { + links.typeOnlyDeclaration = node; + } + else { + const alias = getImmediateAliasedSymbol(symbol); + links.typeOnlyDeclaration = !!alias && getTypeOnlyAliasDeclaration(alias); + } + } + else { + links.typeOnlyDeclaration = false; + } + } + return links.typeOnlyDeclaration || undefined; + } + function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) { const symbol = getSymbolOfNode(node); const target = resolveAlias(symbol); if (target) { const markAlias = target === unknownSymbol || - ((target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target)); + ((target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target) && !getTypeOnlyAliasDeclaration(symbol)); if (markAlias) { markAliasSymbolAsReferenced(symbol); @@ -8250,20 +8194,10 @@ namespace ts { return errorType; } - let type: Type; - let declaration; - if (isTypeOnlyAlias(symbol)) { - // Symbol is synthetic type alias for type-only import or export. - // See `createSyntheticTypeAlias`. - type = getDeclaredTypeOfSymbol(symbol.immediateTarget); - declaration = symbol.valueDeclaration; - } - else { - declaration = Debug.assertDefined(find(symbol.declarations, isTypeAlias), "Type alias symbol with no valid declaration found"); - const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type; - // If typeNode is missing, we will error in checkJSDocTypedefTag. - type = typeNode ? getTypeFromTypeNode(typeNode) : errorType; - } + const declaration = Debug.assertDefined(find(symbol.declarations, isTypeAlias), "Type alias symbol with no valid declaration found"); + const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type; + // If typeNode is missing, we will error in checkJSDocTypedefTag. + let type = typeNode ? getTypeFromTypeNode(typeNode) : errorType; if (popTypeResolution()) { const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); @@ -10994,9 +10928,6 @@ namespace ts { if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { return getTypeFromClassOrInterfaceReference(node, symbol); } - if (isTypeOnlyAlias(symbol)) { - return getTypeReferenceType(node, symbol.immediateTarget); - } if (symbol.flags & SymbolFlags.TypeAlias) { return getTypeFromTypeAliasReference(node, symbol); } @@ -20279,7 +20210,11 @@ namespace ts { } function markAliasReferenced(symbol: Symbol, location: Node) { - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && ((compilerOptions.preserveConstEnums && isExportOrExportExpression(location)) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) { + if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && + !isInTypeQuery(location) && + ((compilerOptions.preserveConstEnums && isExportOrExportExpression(location)) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) && + !getTypeOnlyAliasDeclaration(symbol) + ) { markAliasSymbolAsReferenced(symbol); } } @@ -22858,7 +22793,7 @@ namespace ts { reactSym.isReferenced = SymbolFlags.All; // If react symbol is alias, mark it as refereced - if (reactSym.flags & SymbolFlags.Alias) { + if (reactSym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(reactSym)) { markAliasSymbolAsReferenced(reactSym); } } @@ -29653,7 +29588,8 @@ namespace ts { if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias && symbolIsValue(rootSymbol) - && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) { + && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol)) + && !getTypeOnlyAliasDeclaration(rootSymbol)) { markAliasSymbolAsReferenced(rootSymbol); } } @@ -34224,7 +34160,7 @@ namespace ts { if (isDeclarationNameOrImportPropertyName(node)) { const symbol = getSymbolAtLocation(node); if (symbol) { - return isTypeOnlyImportOrExportName(node) ? getDeclaredTypeOfSymbol(symbol) : getTypeOfSymbol(symbol); + return getTypeOfSymbol(symbol); } return errorType; } @@ -34465,7 +34401,7 @@ namespace ts { const symbol = getReferencedValueSymbol(node); // We should only get the declaration of an alias if there isn't a local value // declaration for the symbol - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value)) { + if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !getTypeOnlyAliasDeclaration(symbol)) { return getDeclarationOfAliasSymbol(symbol); } } @@ -34555,11 +34491,13 @@ namespace ts { function isValueAliasDeclaration(node: Node): boolean { switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: + return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol); case SyntaxKind.ImportClause: case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol); + const symbol = getSymbolOfNode(node) || unknownSymbol; + return isAliasResolvedToValue(symbol) && !getTypeOnlyAliasDeclaration(symbol); case SyntaxKind.ExportDeclaration: const exportClause = (node).exportClause; return !!exportClause && ( @@ -34608,7 +34546,8 @@ namespace ts { } const target = getSymbolLinks(symbol!).target; // TODO: GH#18217 if (target && getModifierFlags(node) & ModifierFlags.Export && - target.flags & SymbolFlags.Value && (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target))) { + target.flags & SymbolFlags.Value && + (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target))) { // An `export import ... =` of a value symbol is always considered referenced return true; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index bd4d56c2a9c7e..63c07aada3d50 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1059,11 +1059,11 @@ "category": "Error", "code": 1360 }, - "Type-only {0} must reference a type, but '{1}' is a value.": { + "'{0}' cannot be used as a value because it was imported using 'import type'.": { "category": "Error", "code": 1361 }, - "Enum '{0}' cannot be used as a value because only its type has been imported.": { + "'{0}' cannot be used as a value because it was exported using 'export type'.": { "category": "Error", "code": 1362 }, @@ -1085,7 +1085,7 @@ }, "Split all invalid type-only imports": { "category": "Message", - "code": 1377 + "code": 1367 }, "Specify emit/checking behavior for imports that are only used for types": { "category": "Message", @@ -1119,6 +1119,14 @@ "category": "Error", "code": 1375 }, + "'{0}' was imported here.": { + "category": "Message", + "code": 1376 + }, + "'{0}' was exported here.": { + "category": "Message", + "code": 1377 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 98f2531b6c6d1..ab8a50a11e015 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4082,6 +4082,7 @@ namespace ts { deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type deferralParent?: Type; // Source union/intersection of a deferred type cjsExportMerged?: Symbol; // Version of the symbol with all non export= exports merged with the export= target + typeOnlyDeclaration?: Identifier | ImportSpecifier | ExportSpecifier | false; // First resolved alias declaration that makes the symbol only usable in type constructs } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d11d3bbd1f5c0..331c13f4d967a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -44,14 +44,6 @@ namespace ts { return (symbol.flags & SymbolFlags.Transient) !== 0; } - export function isTypeOnlyAlias(symbol: Symbol): symbol is TransientSymbol & { immediateTarget: Symbol } { - return isTransientSymbol(symbol) && !!symbol.immediateTarget; - } - - export function isTypeOnlyEnumAlias(symbol: Symbol): ReturnType { - return isTypeOnlyAlias(symbol) && !!(symbol.immediateTarget.flags & SymbolFlags.Enum); - } - const stringWriter = createSingleLineStringWriter(); function createSingleLineStringWriter(): EmitTextWriter { diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index d4bc403b85908..d83b0c97998d0 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1721,16 +1721,14 @@ namespace ts { return isImportSpecifier(node) || isExportSpecifier(node); } - export function isTypeOnlyImportOrExportName(node: Node): boolean { - if (node.kind !== SyntaxKind.Identifier) { - return false; - } - switch (node.parent.kind) { + export function isTypeOnlyImportOrExportName(node: Node): node is Identifier | ImportSpecifier | ExportSpecifier { + switch (node.kind) { + case SyntaxKind.Identifier: + return isImportClause(node.parent) // default import name + || isNamespaceImport(node.parent) && node.parent.parent.isTypeOnly; // namespace import name case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - return (node.parent as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; - case SyntaxKind.ImportClause: - return (node.parent as ImportClause).isTypeOnly; + return (node as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; default: return false; } diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index d71afe7c39553..d14ef1199382d 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -2,10 +2,6 @@ namespace ts.SymbolDisplay { // TODO(drosen): use contextual SemanticMeaning. export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind { - while (isTypeOnlyAlias(symbol)) { - symbol = symbol.immediateTarget; - } - const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location); if (result !== ScriptElementKind.unknown) { return result; @@ -125,11 +121,6 @@ namespace ts.SymbolDisplay { // TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node | undefined, location: Node, semanticMeaning = getMeaningFromLocation(location), alias?: Symbol): SymbolDisplayPartsDocumentationAndSymbolKind { - - while (isTypeOnlyAlias(symbol)) { - symbol = symbol.immediateTarget; - } - const displayParts: SymbolDisplayPart[] = []; let documentation: SymbolDisplayPart[] | undefined; let tags: JSDocTagInfo[] | undefined; diff --git a/tests/baselines/reference/chained.errors.txt b/tests/baselines/reference/chained.errors.txt index 8d684afbb9475..d0e6f2c26f2fa 100644 --- a/tests/baselines/reference/chained.errors.txt +++ b/tests/baselines/reference/chained.errors.txt @@ -1,4 +1,4 @@ -/d.ts(2,5): error TS2693: 'D' only refers to a type, but is being used as a value here. +/d.ts(2,5): error TS1361: 'D' cannot be used as a value because it was imported using 'import type'. /d.ts(3,7): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. @@ -19,7 +19,8 @@ import { D } from './c'; new D(); ~ -!!! error TS2693: 'D' only refers to a type, but is being used as a value here. +!!! error TS1361: 'D' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /c.ts:1:15: 'D' was imported here. const d: D = {}; ~ !!! error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. diff --git a/tests/baselines/reference/chained.symbols b/tests/baselines/reference/chained.symbols index 9ba6d5bed2142..228befe74f543 100644 --- a/tests/baselines/reference/chained.symbols +++ b/tests/baselines/reference/chained.symbols @@ -4,7 +4,7 @@ class A { a!: string } >a : Symbol(A.a, Decl(a.ts, 0, 9)) export type { A as B }; ->A : Symbol(A) +>A : Symbol(A, Decl(a.ts, 0, 0)) >B : Symbol(B, Decl(a.ts, 1, 13)) export type Z = A; @@ -33,6 +33,8 @@ import { D } from './c'; >D : Symbol(D, Decl(d.ts, 0, 8)) new D(); +>D : Symbol(D, Decl(d.ts, 0, 8)) + const d: D = {}; >d : Symbol(d, Decl(d.ts, 2, 5)) >D : Symbol(D, Decl(d.ts, 0, 8)) diff --git a/tests/baselines/reference/chained.types b/tests/baselines/reference/chained.types index 5e52f608b821b..c694bceffd5e1 100644 --- a/tests/baselines/reference/chained.types +++ b/tests/baselines/reference/chained.types @@ -4,7 +4,7 @@ class A { a!: string } >a : string export type { A as B }; ->A : A +>A : typeof A >B : A export type Z = A; @@ -16,26 +16,26 @@ import { Z as Y } from './a'; >Y : any export { B as C } from './a'; ->B : any ->C : any +>B : typeof import("/a").B +>C : typeof import("/a").B === /c.ts === import type { C } from './b'; ->C : A +>C : C export { C as D }; ->C : any ->D : any +>C : typeof C +>D : typeof C === /d.ts === import { D } from './c'; ->D : any +>D : typeof D new D(); ->new D() : any ->D : any +>new D() : D +>D : typeof D const d: D = {}; ->d : A +>d : D >{} : {} diff --git a/tests/baselines/reference/enums.errors.txt b/tests/baselines/reference/enums.errors.txt index 4e874cbee48dc..8644a4dd172e2 100644 --- a/tests/baselines/reference/enums.errors.txt +++ b/tests/baselines/reference/enums.errors.txt @@ -1,5 +1,5 @@ -/b.ts(3,1): error TS1362: Enum 'SyntaxKind' cannot be used as a value because only its type has been imported. -/b.ts(4,1): error TS1362: Enum 'SymbolFlags' cannot be used as a value because only its type has been imported. +/b.ts(3,1): error TS1361: 'SyntaxKind' cannot be used as a value because it was imported using 'import type'. +/b.ts(4,1): error TS1361: 'SymbolFlags' cannot be used as a value because it was imported using 'import type'. ==== /a.ts (0 errors) ==== @@ -21,10 +21,12 @@ SyntaxKind.ImportClause; ~~~~~~~~~~ -!!! error TS1362: Enum 'SyntaxKind' cannot be used as a value because only its type has been imported. +!!! error TS1361: 'SyntaxKind' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /b.ts:1:15: 'SyntaxKind' was imported here. SymbolFlags.Type; ~~~~~~~~~~~ -!!! error TS1362: Enum 'SymbolFlags' cannot be used as a value because only its type has been imported. +!!! error TS1361: 'SymbolFlags' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /b.ts:1:27: 'SymbolFlags' was imported here. let kind: SyntaxKind.ImportClause; let flags: SymbolFlags; diff --git a/tests/baselines/reference/enums.js b/tests/baselines/reference/enums.js index 09243e4a7f134..e71675f8c9c51 100644 --- a/tests/baselines/reference/enums.js +++ b/tests/baselines/reference/enums.js @@ -43,7 +43,7 @@ var SyntaxKind; "use strict"; exports.__esModule = true; SyntaxKind.ImportClause; -SymbolFlags.Type; +"Type" /* Type */; var kind; var flags; //// [c.js] diff --git a/tests/baselines/reference/enums.symbols b/tests/baselines/reference/enums.symbols index edac3617acbee..65e656707ee08 100644 --- a/tests/baselines/reference/enums.symbols +++ b/tests/baselines/reference/enums.symbols @@ -31,11 +31,19 @@ import type { SyntaxKind, SymbolFlags } from './a'; >SymbolFlags : Symbol(SymbolFlags, Decl(b.ts, 0, 25)) SyntaxKind.ImportClause; +>SyntaxKind.ImportClause : Symbol(SyntaxKind.ImportClause, Decl(a.ts, 0, 17)) +>SyntaxKind : Symbol(SyntaxKind, Decl(b.ts, 0, 13)) +>ImportClause : Symbol(SyntaxKind.ImportClause, Decl(a.ts, 0, 17)) + SymbolFlags.Type; +>SymbolFlags.Type : Symbol(SymbolFlags.Type, Decl(a.ts, 5, 24)) +>SymbolFlags : Symbol(SymbolFlags, Decl(b.ts, 0, 25)) +>Type : Symbol(SymbolFlags.Type, Decl(a.ts, 5, 24)) + let kind: SyntaxKind.ImportClause; >kind : Symbol(kind, Decl(b.ts, 4, 3)) >SyntaxKind : Symbol(SyntaxKind, Decl(b.ts, 0, 13)) ->ImportClause : Symbol(SyntaxKind.ImportClause) +>ImportClause : Symbol(SyntaxKind.ImportClause, Decl(a.ts, 0, 17)) let flags: SymbolFlags; >flags : Symbol(flags, Decl(b.ts, 5, 3)) @@ -44,7 +52,7 @@ let flags: SymbolFlags; type TypeFlag = SymbolFlags.Type; >TypeFlag : Symbol(TypeFlag, Decl(b.ts, 5, 23)) >SymbolFlags : Symbol(SymbolFlags, Decl(b.ts, 0, 25)) ->Type : Symbol(SymbolFlags.Type) +>Type : Symbol(SymbolFlags.Type, Decl(a.ts, 5, 24)) export type { TypeFlag }; >TypeFlag : Symbol(TypeFlag, Decl(b.ts, 8, 13)) diff --git a/tests/baselines/reference/enums.types b/tests/baselines/reference/enums.types index ca79cc6147d4a..943e039b26ce0 100644 --- a/tests/baselines/reference/enums.types +++ b/tests/baselines/reference/enums.types @@ -30,31 +30,31 @@ export { SymbolFlags }; === /b.ts === import type { SyntaxKind, SymbolFlags } from './a'; >SyntaxKind : SyntaxKind ->SymbolFlags : import("/a").SymbolFlags +>SymbolFlags : SymbolFlags SyntaxKind.ImportClause; ->SyntaxKind.ImportClause : any ->SyntaxKind : any ->ImportClause : any +>SyntaxKind.ImportClause : SyntaxKind.ImportClause +>SyntaxKind : typeof SyntaxKind +>ImportClause : SyntaxKind.ImportClause SymbolFlags.Type; ->SymbolFlags.Type : any ->SymbolFlags : any ->Type : any +>SymbolFlags.Type : SymbolFlags.Type +>SymbolFlags : typeof SymbolFlags +>Type : SymbolFlags.Type let kind: SyntaxKind.ImportClause; >kind : SyntaxKind.ImportClause >SyntaxKind : any let flags: SymbolFlags; ->flags : import("/a").SymbolFlags +>flags : SymbolFlags type TypeFlag = SymbolFlags.Type; ->TypeFlag : import("/a").SymbolFlags.Type +>TypeFlag : SymbolFlags.Type >SymbolFlags : any export type { TypeFlag }; ->TypeFlag : import("/a").SymbolFlags.Type +>TypeFlag : SymbolFlags.Type === /c.ts === import { SymbolFlags } from './a'; diff --git a/tests/baselines/reference/exportDeclaration.errors.txt b/tests/baselines/reference/exportDeclaration.errors.txt index 387b348843945..6dbd0e8d9c421 100644 --- a/tests/baselines/reference/exportDeclaration.errors.txt +++ b/tests/baselines/reference/exportDeclaration.errors.txt @@ -1,4 +1,4 @@ -/b.ts(3,5): error TS2693: 'A' only refers to a type, but is being used as a value here. +/b.ts(3,5): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. ==== /a.ts (0 errors) ==== @@ -10,5 +10,6 @@ declare const a: A; new A(); ~ -!!! error TS2693: 'A' only refers to a type, but is being used as a value here. +!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 /a.ts:2:15: 'A' was exported here. \ No newline at end of file diff --git a/tests/baselines/reference/exportDeclaration.symbols b/tests/baselines/reference/exportDeclaration.symbols index c35f92e71592e..39b0f4755dfda 100644 --- a/tests/baselines/reference/exportDeclaration.symbols +++ b/tests/baselines/reference/exportDeclaration.symbols @@ -14,4 +14,5 @@ declare const a: A; >A : Symbol(A, Decl(b.ts, 0, 8)) new A(); +>A : Symbol(A, Decl(b.ts, 0, 8)) diff --git a/tests/baselines/reference/exportDeclaration.types b/tests/baselines/reference/exportDeclaration.types index 277082b8586a8..69c1aab7dd9a5 100644 --- a/tests/baselines/reference/exportDeclaration.types +++ b/tests/baselines/reference/exportDeclaration.types @@ -7,12 +7,12 @@ export type { A }; === /b.ts === import { A } from './a'; ->A : any +>A : typeof A declare const a: A; >a : A new A(); ->new A() : any ->A : any +>new A() : A +>A : typeof A diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier.errors.txt b/tests/baselines/reference/exportDeclaration_moduleSpecifier.errors.txt index 6ef18aad95986..39cb01ddec045 100644 --- a/tests/baselines/reference/exportDeclaration_moduleSpecifier.errors.txt +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier.errors.txt @@ -1,4 +1,4 @@ -/c.ts(3,5): error TS2693: 'A' only refers to a type, but is being used as a value here. +/c.ts(3,5): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. ==== /a.ts (0 errors) ==== @@ -12,5 +12,6 @@ declare const a: A; new A(); ~ -!!! error TS2693: 'A' only refers to a type, but is being used as a value here. +!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 /b.ts:1:15: 'A' was exported here. \ No newline at end of file diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier.symbols b/tests/baselines/reference/exportDeclaration_moduleSpecifier.symbols index e4917cddc066d..b24d2e93c0a4f 100644 --- a/tests/baselines/reference/exportDeclaration_moduleSpecifier.symbols +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier.symbols @@ -15,4 +15,5 @@ declare const a: A; >A : Symbol(A, Decl(c.ts, 0, 8)) new A(); +>A : Symbol(A, Decl(c.ts, 0, 8)) diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier.types b/tests/baselines/reference/exportDeclaration_moduleSpecifier.types index b870b2804f582..bc3feebdd9fbe 100644 --- a/tests/baselines/reference/exportDeclaration_moduleSpecifier.types +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier.types @@ -8,12 +8,12 @@ export type { A } from './a'; === /c.ts === import { A } from './b'; ->A : any +>A : typeof A declare const a: A; ->a : import("/a").A +>a : A new A(); ->new A() : any ->A : any +>new A() : A +>A : typeof A diff --git a/tests/baselines/reference/exportDeclaration_value.errors.txt b/tests/baselines/reference/exportDeclaration_value.errors.txt deleted file mode 100644 index d759ad8c7c57b..0000000000000 --- a/tests/baselines/reference/exportDeclaration_value.errors.txt +++ /dev/null @@ -1,18 +0,0 @@ -/a.ts(2,15): error TS1361: Type-only export must reference a type, but 'A' is a value. -/b.ts(1,15): error TS1361: Type-only export must reference a type, but 'AA' is a value. - - -==== /a.ts (1 errors) ==== - const A = {}; - export type { A }; - ~ -!!! error TS1361: Type-only export must reference a type, but 'A' is a value. -!!! related TS2728 /a.ts:1:7: 'A' is declared here. - export const AA = {}; - -==== /b.ts (1 errors) ==== - export type { AA } from './a'; - ~~ -!!! error TS1361: Type-only export must reference a type, but 'AA' is a value. -!!! related TS2728 /a.ts:3:14: 'AA' is declared here. - \ No newline at end of file diff --git a/tests/baselines/reference/filterNamespace_import.errors.txt b/tests/baselines/reference/filterNamespace_import.errors.txt deleted file mode 100644 index b9e9a09f8c790..0000000000000 --- a/tests/baselines/reference/filterNamespace_import.errors.txt +++ /dev/null @@ -1,30 +0,0 @@ -/a.ts(2,1): error TS2708: Cannot use namespace 'ns' as a value. -/a.ts(3,1): error TS2708: Cannot use namespace 'ns' as a value. - - -==== /ns.ts (0 errors) ==== - namespace ns { - export type Type = string; - export class Class {} - export const Value = ""; - export namespace nested { - export class NestedClass { - a!: string; - } - } - } - - export default ns; - -==== /a.ts (2 errors) ==== - import type ns from './ns'; - ns.Class; // Error - ~~ -!!! error TS2708: Cannot use namespace 'ns' as a value. - ns.Value; // Error - ~~ -!!! error TS2708: Cannot use namespace 'ns' as a value. - let c: ns.Class; - let t: ns.Type = ""; - let n: ns.nested.NestedClass = { a: '' }; - \ No newline at end of file diff --git a/tests/baselines/reference/filterNamespace_import.js b/tests/baselines/reference/filterNamespace_import.js index 094cabd326760..16340f4bad764 100644 --- a/tests/baselines/reference/filterNamespace_import.js +++ b/tests/baselines/reference/filterNamespace_import.js @@ -49,8 +49,8 @@ exports["default"] = ns; //// [a.js] "use strict"; exports.__esModule = true; -ns.Class; // Error -ns.Value; // Error +ns_1["default"].Class; // Error +ns_1["default"].Value; // Error var c; var t = ""; var n = { a: '' }; diff --git a/tests/baselines/reference/filterNamespace_import.symbols b/tests/baselines/reference/filterNamespace_import.symbols index f38ea7cc983c9..889fa5fe1155a 100644 --- a/tests/baselines/reference/filterNamespace_import.symbols +++ b/tests/baselines/reference/filterNamespace_import.symbols @@ -31,11 +31,19 @@ import type ns from './ns'; >ns : Symbol(ns, Decl(a.ts, 0, 6)) ns.Class; // Error +>ns.Class : Symbol(ns.Class, Decl(ns.ts, 1, 28)) +>ns : Symbol(ns, Decl(a.ts, 0, 6)) +>Class : Symbol(ns.Class, Decl(ns.ts, 1, 28)) + ns.Value; // Error +>ns.Value : Symbol(ns.Value, Decl(ns.ts, 3, 14)) +>ns : Symbol(ns, Decl(a.ts, 0, 6)) +>Value : Symbol(ns.Value, Decl(ns.ts, 3, 14)) + let c: ns.Class; >c : Symbol(c, Decl(a.ts, 3, 3)) >ns : Symbol(ns, Decl(a.ts, 0, 6)) ->Class : Symbol(ns.Class) +>Class : Symbol(ns.Class, Decl(ns.ts, 1, 28)) let t: ns.Type = ""; >t : Symbol(t, Decl(a.ts, 4, 3)) @@ -46,6 +54,6 @@ let n: ns.nested.NestedClass = { a: '' }; >n : Symbol(n, Decl(a.ts, 5, 3)) >ns : Symbol(ns, Decl(a.ts, 0, 6)) >nested : Symbol(ns.nested, Decl(ns.ts, 3, 26)) ->NestedClass : Symbol(NestedClass) +>NestedClass : Symbol(ns.nested.NestedClass, Decl(ns.ts, 4, 27)) >a : Symbol(a, Decl(a.ts, 5, 32)) diff --git a/tests/baselines/reference/filterNamespace_import.types b/tests/baselines/reference/filterNamespace_import.types index ef9ad8a6e269b..162de035c4cda 100644 --- a/tests/baselines/reference/filterNamespace_import.types +++ b/tests/baselines/reference/filterNamespace_import.types @@ -32,17 +32,17 @@ import type ns from './ns'; >ns : any ns.Class; // Error ->ns.Class : any ->ns : any ->Class : any +>ns.Class : typeof ns.Class +>ns : typeof ns +>Class : typeof ns.Class ns.Value; // Error ->ns.Value : any ->ns : any ->Value : any +>ns.Value : "" +>ns : typeof ns +>Value : "" let c: ns.Class; ->c : import("/ns").default.Class +>c : ns.Class >ns : any let t: ns.Type = ""; @@ -51,7 +51,7 @@ let t: ns.Type = ""; >"" : "" let n: ns.nested.NestedClass = { a: '' }; ->n : import("/ns").default.nested.NestedClass +>n : ns.nested.NestedClass >ns : any >nested : any >{ a: '' } : { a: string; } diff --git a/tests/baselines/reference/generic.symbols b/tests/baselines/reference/generic.symbols index 88625de90732d..a3503b971269d 100644 --- a/tests/baselines/reference/generic.symbols +++ b/tests/baselines/reference/generic.symbols @@ -6,7 +6,7 @@ export class A { a!: T } >T : Symbol(T, Decl(a.ts, 0, 15)) export type { A as B }; ->A : Symbol(A) +>A : Symbol(A, Decl(a.ts, 0, 0)) >B : Symbol(B, Decl(a.ts, 1, 13)) === /b.ts === diff --git a/tests/baselines/reference/generic.types b/tests/baselines/reference/generic.types index abe6f1da4a0a5..04bced36a04a7 100644 --- a/tests/baselines/reference/generic.types +++ b/tests/baselines/reference/generic.types @@ -4,30 +4,30 @@ export class A { a!: T } >a : T export type { A as B }; ->A : A +>A : typeof A >B : A === /b.ts === import type { A } from './a'; ->A : import("/a").A +>A : A import { B } from './a'; ->B : any +>B : typeof A let a: A = { a: "" }; ->a : import("/a").A +>a : A >{ a: "" } : { a: string; } >a : string >"" : "" let b: B = { a: 3 }; ->b : import("/a").A +>b : A >{ a: 3 } : { a: number; } >a : number >3 : 3 let c: A = {}; ->c : import("/a").A +>c : A >{} : {} let d: B = { a: "" }; diff --git a/tests/baselines/reference/importClause_default.errors.txt b/tests/baselines/reference/importClause_default.errors.txt deleted file mode 100644 index 99345c4102f4c..0000000000000 --- a/tests/baselines/reference/importClause_default.errors.txt +++ /dev/null @@ -1,13 +0,0 @@ -/b.ts(2,5): error TS2693: 'A' only refers to a type, but is being used as a value here. - - -==== /a.ts (0 errors) ==== - export default class A { a!: string } - -==== /b.ts (1 errors) ==== - import type A from './a'; - new A(); - ~ -!!! error TS2693: 'A' only refers to a type, but is being used as a value here. - let a: A = { a: '' }; - \ No newline at end of file diff --git a/tests/baselines/reference/importClause_default.js b/tests/baselines/reference/importClause_default.js index d9e7c54331d4f..c0ad74330eb4b 100644 --- a/tests/baselines/reference/importClause_default.js +++ b/tests/baselines/reference/importClause_default.js @@ -21,5 +21,5 @@ exports["default"] = A; //// [b.js] "use strict"; exports.__esModule = true; -new A(); +new a_1["default"](); var a = { a: '' }; diff --git a/tests/baselines/reference/importClause_default.symbols b/tests/baselines/reference/importClause_default.symbols index 3c13a061eaa23..88d8f830234d1 100644 --- a/tests/baselines/reference/importClause_default.symbols +++ b/tests/baselines/reference/importClause_default.symbols @@ -8,6 +8,8 @@ import type A from './a'; >A : Symbol(A, Decl(b.ts, 0, 6)) new A(); +>A : Symbol(A, Decl(b.ts, 0, 6)) + let a: A = { a: '' }; >a : Symbol(a, Decl(b.ts, 2, 3)) >A : Symbol(A, Decl(b.ts, 0, 6)) diff --git a/tests/baselines/reference/importClause_default.types b/tests/baselines/reference/importClause_default.types index 47ab1c72d607a..f924c2c22f959 100644 --- a/tests/baselines/reference/importClause_default.types +++ b/tests/baselines/reference/importClause_default.types @@ -5,14 +5,14 @@ export default class A { a!: string } === /b.ts === import type A from './a'; ->A : import("/a").default +>A : A new A(); ->new A() : any ->A : any +>new A() : A +>A : typeof A let a: A = { a: '' }; ->a : import("/a").default +>a : A >{ a: '' } : { a: string; } >a : string >'' : "" diff --git a/tests/baselines/reference/importClause_namedImports.errors.txt b/tests/baselines/reference/importClause_namedImports.errors.txt index c5cc2bb326006..b33a4fe5bc6ab 100644 --- a/tests/baselines/reference/importClause_namedImports.errors.txt +++ b/tests/baselines/reference/importClause_namedImports.errors.txt @@ -1,5 +1,4 @@ -/d.ts(1,21): error TS1361: Type-only import must reference a type, but 'C' is a value. -/d.ts(2,5): error TS2693: 'A' only refers to a type, but is being used as a value here. +/d.ts(2,5): error TS1361: 'A' cannot be used as a value because it was imported using 'import type'. ==== /abc.ts (0 errors) ==== @@ -7,14 +6,12 @@ export type B = { b: string }; export const C = ""; -==== /d.ts (2 errors) ==== +==== /d.ts (1 errors) ==== import type { A, B, C } from './abc'; - ~ -!!! error TS1361: Type-only import must reference a type, but 'C' is a value. -!!! related TS2728 /abc.ts:3:14: 'C' is declared here. new A(); ~ -!!! error TS2693: 'A' only refers to a type, but is being used as a value here. +!!! error TS1361: 'A' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /d.ts:1:15: 'A' was imported here. declare let a: A; declare let b: B; b.b; diff --git a/tests/baselines/reference/importClause_namedImports.symbols b/tests/baselines/reference/importClause_namedImports.symbols index a21e8d8cba2bb..077b86f4c0126 100644 --- a/tests/baselines/reference/importClause_namedImports.symbols +++ b/tests/baselines/reference/importClause_namedImports.symbols @@ -16,6 +16,8 @@ import type { A, B, C } from './abc'; >C : Symbol(C, Decl(d.ts, 0, 19)) new A(); +>A : Symbol(A, Decl(d.ts, 0, 13)) + declare let a: A; >a : Symbol(a, Decl(d.ts, 2, 11)) >A : Symbol(A, Decl(d.ts, 0, 13)) diff --git a/tests/baselines/reference/importClause_namedImports.types b/tests/baselines/reference/importClause_namedImports.types index a9c43fcf582ad..8a96772a677f5 100644 --- a/tests/baselines/reference/importClause_namedImports.types +++ b/tests/baselines/reference/importClause_namedImports.types @@ -12,16 +12,16 @@ export const C = ""; === /d.ts === import type { A, B, C } from './abc'; ->A : import("/abc").A +>A : A >B : B >C : any new A(); ->new A() : any ->A : any +>new A() : A +>A : typeof A declare let a: A; ->a : import("/abc").A +>a : A declare let b: B; >b : B diff --git a/tests/baselines/reference/importClause_namespaceImport.errors.txt b/tests/baselines/reference/importClause_namespaceImport.errors.txt index 04d8dceddd02c..18f77dc29530f 100644 --- a/tests/baselines/reference/importClause_namespaceImport.errors.txt +++ b/tests/baselines/reference/importClause_namespaceImport.errors.txt @@ -1,5 +1,3 @@ -/b.ts(2,1): error TS2708: Cannot use namespace 'types' as a value. -/b.ts(3,1): error TS2708: Cannot use namespace 'types' as a value. /b.ts(4,14): error TS2694: Namespace '"/a"' has no exported member 'Value'. /b.ts(5,7): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. /b.ts(6,7): error TS2741: Property 'b' is missing in type '{}' but required in type 'B'. @@ -11,14 +9,10 @@ export type C = T; export const Value = {}; -==== /b.ts (5 errors) ==== +==== /b.ts (3 errors) ==== import type * as types from './a'; types; - ~~~~~ -!!! error TS2708: Cannot use namespace 'types' as a value. types.Value; - ~~~~~ -!!! error TS2708: Cannot use namespace 'types' as a value. let v: types.Value; ~~~~~ !!! error TS2694: Namespace '"/a"' has no exported member 'Value'. diff --git a/tests/baselines/reference/importClause_namespaceImport.symbols b/tests/baselines/reference/importClause_namespaceImport.symbols index 4242b05a4ca9d..f2133f201cbba 100644 --- a/tests/baselines/reference/importClause_namespaceImport.symbols +++ b/tests/baselines/reference/importClause_namespaceImport.symbols @@ -20,7 +20,13 @@ import type * as types from './a'; >types : Symbol(types, Decl(b.ts, 0, 11)) types; +>types : Symbol(types, Decl(b.ts, 0, 11)) + types.Value; +>types.Value : Symbol(types.Value, Decl(a.ts, 3, 12)) +>types : Symbol(types, Decl(b.ts, 0, 11)) +>Value : Symbol(types.Value, Decl(a.ts, 3, 12)) + let v: types.Value; >v : Symbol(v, Decl(b.ts, 3, 3)) >types : Symbol(types, Decl(b.ts, 0, 11)) @@ -28,12 +34,12 @@ let v: types.Value; const a: types.A = {}; >a : Symbol(a, Decl(b.ts, 4, 5)) >types : Symbol(types, Decl(b.ts, 0, 11)) ->A : Symbol(types.A) +>A : Symbol(types.A, Decl(a.ts, 0, 0)) const b: types.B = {}; >b : Symbol(b, Decl(b.ts, 5, 5)) >types : Symbol(types, Decl(b.ts, 0, 11)) ->B : Symbol(types.B) +>B : Symbol(types.B, Decl(a.ts, 0, 29)) const c: types.C = ""; >c : Symbol(c, Decl(b.ts, 6, 5)) diff --git a/tests/baselines/reference/importClause_namespaceImport.types b/tests/baselines/reference/importClause_namespaceImport.types index 319edbd5632c3..471bf17367720 100644 --- a/tests/baselines/reference/importClause_namespaceImport.types +++ b/tests/baselines/reference/importClause_namespaceImport.types @@ -16,27 +16,27 @@ export const Value = {}; === /b.ts === import type * as types from './a'; ->types : any +>types : typeof types types; ->types : any +>types : typeof types types.Value; ->types.Value : any ->types : any ->Value : any +>types.Value : {} +>types : typeof types +>Value : {} let v: types.Value; >v : any >types : any const a: types.A = {}; ->a : import("/a").A +>a : types.A >types : any >{} : {} const b: types.B = {}; ->b : import("/a").B +>b : types.B >types : any >{} : {} diff --git a/tests/baselines/reference/renamed.symbols b/tests/baselines/reference/renamed.symbols index 8af253a8c0370..3a39567c2d7c3 100644 --- a/tests/baselines/reference/renamed.symbols +++ b/tests/baselines/reference/renamed.symbols @@ -4,7 +4,7 @@ class A { a!: string } >a : Symbol(A.a, Decl(a.ts, 0, 9)) export type { A as B }; ->A : Symbol(A) +>A : Symbol(A, Decl(a.ts, 0, 0)) >B : Symbol(B, Decl(a.ts, 1, 13)) === /b.ts === diff --git a/tests/baselines/reference/renamed.types b/tests/baselines/reference/renamed.types index 85a9bfcc4002c..391c686dae7eb 100644 --- a/tests/baselines/reference/renamed.types +++ b/tests/baselines/reference/renamed.types @@ -4,20 +4,20 @@ class A { a!: string } >a : string export type { A as B }; ->A : A +>A : typeof A >B : A === /b.ts === export type { B as C } from './a'; ->B : A ->C : A +>B : typeof import("/a").B +>C : import("/a").B === /c.ts === import type { C as D } from './b'; ->C : A ->D : A +>C : typeof D +>D : D const d: D = {}; ->d : A +>d : D >{} : {} diff --git a/tests/baselines/reference/typeQuery.js b/tests/baselines/reference/typeQuery.js new file mode 100644 index 0000000000000..8ad78d2fd10dc --- /dev/null +++ b/tests/baselines/reference/typeQuery.js @@ -0,0 +1,23 @@ +//// [tests/cases/conformance/externalModules/typeOnly/typeQuery.ts] //// + +//// [a.ts] +export class A { } + +//// [b.ts] +import type { A } from './a'; +let AConstructor: typeof A; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +exports.A = A; +//// [b.js] +"use strict"; +exports.__esModule = true; +var AConstructor; diff --git a/tests/baselines/reference/typeQuery.symbols b/tests/baselines/reference/typeQuery.symbols new file mode 100644 index 0000000000000..960eddb71a4f9 --- /dev/null +++ b/tests/baselines/reference/typeQuery.symbols @@ -0,0 +1,12 @@ +=== /a.ts === +export class A { } +>A : Symbol(A, Decl(a.ts, 0, 0)) + +=== /b.ts === +import type { A } from './a'; +>A : Symbol(A, Decl(b.ts, 0, 13)) + +let AConstructor: typeof A; +>AConstructor : Symbol(AConstructor, Decl(b.ts, 1, 3)) +>A : Symbol(A, Decl(b.ts, 0, 13)) + diff --git a/tests/baselines/reference/typeQuery.types b/tests/baselines/reference/typeQuery.types new file mode 100644 index 0000000000000..f1218cbc85eff --- /dev/null +++ b/tests/baselines/reference/typeQuery.types @@ -0,0 +1,12 @@ +=== /a.ts === +export class A { } +>A : A + +=== /b.ts === +import type { A } from './a'; +>A : A + +let AConstructor: typeof A; +>AConstructor : typeof A +>A : typeof A + diff --git a/tests/cases/conformance/externalModules/typeOnly/typeQuery.ts b/tests/cases/conformance/externalModules/typeOnly/typeQuery.ts new file mode 100644 index 0000000000000..ad481dbd272c9 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/typeQuery.ts @@ -0,0 +1,6 @@ +// @Filename: /a.ts +export class A { } + +// @Filename: /b.ts +import type { A } from './a'; +let AConstructor: typeof A; From 2ae9edc2c98ddbd95a88499ebb6a168fdb1fba06 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 8 Jan 2020 13:32:05 -0800 Subject: [PATCH 02/24] =?UTF-8?q?Don=E2=80=99t=20error=20using=20type-only?= =?UTF-8?q?=20import=20in=20ambient=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/compiler/checker.ts | 2 +- tests/baselines/reference/ambient.js | 25 +++++++++++++++++++ tests/baselines/reference/ambient.symbols | 21 ++++++++++++++++ tests/baselines/reference/ambient.types | 21 ++++++++++++++++ .../externalModules/typeOnly/ambient.ts | 9 +++++++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/ambient.js create mode 100644 tests/baselines/reference/ambient.symbols create mode 100644 tests/baselines/reference/ambient.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/ambient.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ebe44d35fa5e..f63d6d4e70618 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1851,7 +1851,7 @@ namespace ts { error(errorLocation, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializer.name), declarationNameToString(errorLocation)); } } - if (result && errorLocation && meaning & SymbolFlags.Value && isInExpressionContext(errorLocation)) { + if (result && errorLocation && meaning & SymbolFlags.Value && !(errorLocation.flags & NodeFlags.Ambient) && isInExpressionContext(errorLocation)) { const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result); if (typeOnlyDeclaration) { const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier diff --git a/tests/baselines/reference/ambient.js b/tests/baselines/reference/ambient.js new file mode 100644 index 0000000000000..09f24592fcf0f --- /dev/null +++ b/tests/baselines/reference/ambient.js @@ -0,0 +1,25 @@ +//// [tests/cases/conformance/externalModules/typeOnly/ambient.ts] //// + +//// [a.ts] +export class A { a!: string } + +//// [b.ts] +import type { A } from './a'; +declare class B extends A {} +declare namespace ns { + class C extends A {} +} + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +exports.A = A; +//// [b.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/ambient.symbols b/tests/baselines/reference/ambient.symbols new file mode 100644 index 0000000000000..ce7d34b405286 --- /dev/null +++ b/tests/baselines/reference/ambient.symbols @@ -0,0 +1,21 @@ +=== /a.ts === +export class A { a!: string } +>A : Symbol(A, Decl(a.ts, 0, 0)) +>a : Symbol(A.a, Decl(a.ts, 0, 16)) + +=== /b.ts === +import type { A } from './a'; +>A : Symbol(A, Decl(b.ts, 0, 13)) + +declare class B extends A {} +>B : Symbol(B, Decl(b.ts, 0, 29)) +>A : Symbol(A, Decl(b.ts, 0, 13)) + +declare namespace ns { +>ns : Symbol(ns, Decl(b.ts, 1, 28)) + + class C extends A {} +>C : Symbol(C, Decl(b.ts, 2, 22)) +>A : Symbol(A, Decl(b.ts, 0, 13)) +} + diff --git a/tests/baselines/reference/ambient.types b/tests/baselines/reference/ambient.types new file mode 100644 index 0000000000000..8f6695e7482b1 --- /dev/null +++ b/tests/baselines/reference/ambient.types @@ -0,0 +1,21 @@ +=== /a.ts === +export class A { a!: string } +>A : A +>a : string + +=== /b.ts === +import type { A } from './a'; +>A : A + +declare class B extends A {} +>B : B +>A : A + +declare namespace ns { +>ns : typeof ns + + class C extends A {} +>C : C +>A : A +} + diff --git a/tests/cases/conformance/externalModules/typeOnly/ambient.ts b/tests/cases/conformance/externalModules/typeOnly/ambient.ts new file mode 100644 index 0000000000000..2632018f7e2c5 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/ambient.ts @@ -0,0 +1,9 @@ +// @Filename: /a.ts +export class A { a!: string } + +// @Filename: /b.ts +import type { A } from './a'; +declare class B extends A {} +declare namespace ns { + class C extends A {} +} From db93eb23034b300f1a01e151f106be80b9daf35e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 8 Jan 2020 13:40:12 -0800 Subject: [PATCH 03/24] Fix default import --- src/compiler/utilitiesPublic.ts | 2 ++ .../reference/importClause_default.errors.txt | 14 ++++++++++++++ tests/baselines/reference/importClause_default.js | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/importClause_default.errors.txt diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index d83b0c97998d0..5a95476cdecc7 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1729,6 +1729,8 @@ namespace ts { case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: return (node as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; + case SyntaxKind.ImportClause: + return (node as ImportClause).isTypeOnly; default: return false; } diff --git a/tests/baselines/reference/importClause_default.errors.txt b/tests/baselines/reference/importClause_default.errors.txt new file mode 100644 index 0000000000000..66e742f8cd7bd --- /dev/null +++ b/tests/baselines/reference/importClause_default.errors.txt @@ -0,0 +1,14 @@ +/b.ts(2,5): error TS1361: 'A' cannot be used as a value because it was imported using 'import type'. + + +==== /a.ts (0 errors) ==== + export default class A { a!: string } + +==== /b.ts (1 errors) ==== + import type A from './a'; + new A(); + ~ +!!! error TS1361: 'A' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /b.ts:1:8: 'A' was imported here. + let a: A = { a: '' }; + \ No newline at end of file diff --git a/tests/baselines/reference/importClause_default.js b/tests/baselines/reference/importClause_default.js index c0ad74330eb4b..d9e7c54331d4f 100644 --- a/tests/baselines/reference/importClause_default.js +++ b/tests/baselines/reference/importClause_default.js @@ -21,5 +21,5 @@ exports["default"] = A; //// [b.js] "use strict"; exports.__esModule = true; -new a_1["default"](); +new A(); var a = { a: '' }; From a2548c862744c763b0fec52d9061b7e298bbe034 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 8 Jan 2020 13:46:39 -0800 Subject: [PATCH 04/24] Fix namespace import --- src/compiler/utilitiesPublic.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 5a95476cdecc7..34507363a39f7 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1729,6 +1729,8 @@ namespace ts { case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: return (node as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; + case SyntaxKind.NamespaceImport: + return (node as NamespaceImport).parent.isTypeOnly; case SyntaxKind.ImportClause: return (node as ImportClause).isTypeOnly; default: From 8d3f167ff833584a39b16820d47eefa3744b9da8 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 8 Jan 2020 13:53:04 -0800 Subject: [PATCH 05/24] Update more baselines --- .../reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- .../filterNamespace_import.errors.txt | 32 +++++++++++++++++++ .../reference/filterNamespace_import.js | 4 +-- .../importClause_namespaceImport.errors.txt | 10 +++++- 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/filterNamespace_import.errors.txt diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 5ca50db806704..a8d10d88f27ad 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3739,7 +3739,7 @@ declare namespace ts { function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken; function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail; function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier; - function isTypeOnlyImportOrExportName(node: Node): boolean; + function isTypeOnlyImportOrExportName(node: Node): node is Identifier | ImportSpecifier | ExportSpecifier; function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken; function isModifier(node: Node): node is Modifier; function isEntityName(node: Node): node is EntityName; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 1143d4c6ebceb..06c3190a266c9 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3739,7 +3739,7 @@ declare namespace ts { function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken; function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail; function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier; - function isTypeOnlyImportOrExportName(node: Node): boolean; + function isTypeOnlyImportOrExportName(node: Node): node is Identifier | ImportSpecifier | ExportSpecifier; function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken; function isModifier(node: Node): node is Modifier; function isEntityName(node: Node): node is EntityName; diff --git a/tests/baselines/reference/filterNamespace_import.errors.txt b/tests/baselines/reference/filterNamespace_import.errors.txt new file mode 100644 index 0000000000000..916504c4879cd --- /dev/null +++ b/tests/baselines/reference/filterNamespace_import.errors.txt @@ -0,0 +1,32 @@ +/a.ts(2,1): error TS1361: 'ns' cannot be used as a value because it was imported using 'import type'. +/a.ts(3,1): error TS1361: 'ns' cannot be used as a value because it was imported using 'import type'. + + +==== /ns.ts (0 errors) ==== + namespace ns { + export type Type = string; + export class Class {} + export const Value = ""; + export namespace nested { + export class NestedClass { + a!: string; + } + } + } + + export default ns; + +==== /a.ts (2 errors) ==== + import type ns from './ns'; + ns.Class; // Error + ~~ +!!! error TS1361: 'ns' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /a.ts:1:8: 'ns' was imported here. + ns.Value; // Error + ~~ +!!! error TS1361: 'ns' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /a.ts:1:8: 'ns' was imported here. + let c: ns.Class; + let t: ns.Type = ""; + let n: ns.nested.NestedClass = { a: '' }; + \ No newline at end of file diff --git a/tests/baselines/reference/filterNamespace_import.js b/tests/baselines/reference/filterNamespace_import.js index 16340f4bad764..094cabd326760 100644 --- a/tests/baselines/reference/filterNamespace_import.js +++ b/tests/baselines/reference/filterNamespace_import.js @@ -49,8 +49,8 @@ exports["default"] = ns; //// [a.js] "use strict"; exports.__esModule = true; -ns_1["default"].Class; // Error -ns_1["default"].Value; // Error +ns.Class; // Error +ns.Value; // Error var c; var t = ""; var n = { a: '' }; diff --git a/tests/baselines/reference/importClause_namespaceImport.errors.txt b/tests/baselines/reference/importClause_namespaceImport.errors.txt index 18f77dc29530f..a4c543d7f96a5 100644 --- a/tests/baselines/reference/importClause_namespaceImport.errors.txt +++ b/tests/baselines/reference/importClause_namespaceImport.errors.txt @@ -1,3 +1,5 @@ +/b.ts(2,1): error TS1361: 'types' cannot be used as a value because it was imported using 'import type'. +/b.ts(3,1): error TS1361: 'types' cannot be used as a value because it was imported using 'import type'. /b.ts(4,14): error TS2694: Namespace '"/a"' has no exported member 'Value'. /b.ts(5,7): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. /b.ts(6,7): error TS2741: Property 'b' is missing in type '{}' but required in type 'B'. @@ -9,10 +11,16 @@ export type C = T; export const Value = {}; -==== /b.ts (3 errors) ==== +==== /b.ts (5 errors) ==== import type * as types from './a'; types; + ~~~~~ +!!! error TS1361: 'types' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /b.ts:1:13: 'types' was imported here. types.Value; + ~~~~~ +!!! error TS1361: 'types' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /b.ts:1:13: 'types' was imported here. let v: types.Value; ~~~~~ !!! error TS2694: Namespace '"/a"' has no exported member 'Value'. From b56ad7d16a536e46efff1f323d743802926a4741 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 8 Jan 2020 13:59:20 -0800 Subject: [PATCH 06/24] Prevent circular resolution --- src/compiler/checker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f63d6d4e70618..33d441d6ae1d5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2486,6 +2486,7 @@ namespace ts { } else { const alias = getImmediateAliasedSymbol(symbol); + links.typeOnlyDeclaration = false; // Prevents circular resolution possible in error scenarios links.typeOnlyDeclaration = !!alias && getTypeOnlyAliasDeclaration(alias); } } From 0547a3dc2dfdcbd3d52ac37827d5e1a1f802bce2 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 8 Jan 2020 16:20:51 -0800 Subject: [PATCH 07/24] Track const enum expression usage --- src/compiler/checker.ts | 43 +++++++++++-------- src/compiler/types.ts | 3 +- src/compiler/utilities.ts | 13 ++++++ .../typeOnly/importsNotUsedAsValues_error.ts | 15 +++++++ 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 33d441d6ae1d5..c4d6afe495000 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2532,6 +2532,15 @@ namespace ts { } } + // Aliases that resolve to const enums are not marked as referenced because they are not emitted, + // but their usage in value positions must be tracked to determine if the import can be type-only. + function markConstEnumAliasAsReferenced(symbol: Symbol) { + const links = getSymbolLinks(symbol); + if (!links.constEnumReferenced) { + links.constEnumReferenced = true; + } + } + // This function is only for imports with entity names function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, dontResolveAlias?: boolean): Symbol | undefined { // There are three things we might try to look for. In the following examples, @@ -20211,12 +20220,13 @@ namespace ts { } function markAliasReferenced(symbol: Symbol, location: Node) { - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && - !isInTypeQuery(location) && - ((compilerOptions.preserveConstEnums && isExportOrExportExpression(location)) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) && - !getTypeOnlyAliasDeclaration(symbol) - ) { - markAliasSymbolAsReferenced(symbol); + if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !getTypeOnlyAliasDeclaration(symbol)) { + if (compilerOptions.preserveConstEnums && isExportOrExportExpression(location) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) { + markAliasSymbolAsReferenced(symbol); + } + else { + markConstEnumAliasAsReferenced(symbol); + } } } @@ -33045,17 +33055,15 @@ namespace ts { } function importClauseContainsReferencedImport(importClause: ImportClause) { - return importClause.name && isReferenced(importClause) - || importClause.namedBindings && namedBindingsContainsReferencedImport(importClause.namedBindings); + return forEachImportClauseDeclaration(importClause, declaration => { + return !!getSymbolOfNode(declaration).isReferenced; + }); + } - function isReferenced(declaration: Declaration) { - return !!getMergedSymbol(getSymbolOfNode(declaration)).isReferenced; - } - function namedBindingsContainsReferencedImport(namedBindings: NamedImportBindings) { - return isNamespaceImport(namedBindings) - ? isReferenced(namedBindings) - : some(namedBindings.elements, isReferenced); - } + function importClauseContainsConstEnumUsedAsValue(importClause: ImportClause) { + return forEachImportClauseDeclaration(importClause, declaration => { + return !!getSymbolLinks(getSymbolOfNode(declaration)).constEnumReferenced; + }); } function checkImportsForTypeOnlyConversion(sourceFile: SourceFile) { @@ -33065,7 +33073,8 @@ namespace ts { statement.importClause && !statement.importClause.isTypeOnly && importClauseContainsReferencedImport(statement.importClause) && - !isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) + !isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) && + !importClauseContainsConstEnumUsedAsValue(statement.importClause) ) { const isError = compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Error; errorOrSuggestion( diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ab8a50a11e015..4015253d39914 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4059,7 +4059,8 @@ namespace ts { instantiations?: Map; // Instantiations of generic type alias (undefined if non-generic) inferredClassSymbol?: Map; // Symbol of an inferred ES5 constructor function mapper?: TypeMapper; // Type mapper for instantiation alias - referenced?: boolean; // True if alias symbol has been referenced as a value + referenced?: boolean; // True if alias symbol has been referenced as a value that can be emitted + constEnumReferenced?: boolean; // True if alias symbol resolves to a const enum and is referenced as a value ('referenced' will be false) containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property leftSpread?: Symbol; // Left source for synthetic spread property rightSpread?: Symbol; // Right source for synthetic spread property diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 331c13f4d967a..3c19ae11d9d93 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2273,6 +2273,19 @@ namespace ts { return node.kind === SyntaxKind.ImportDeclaration && !!node.importClause && !!node.importClause.name; } + export function forEachImportClauseDeclaration(node: ImportClause, action: (declaration: ImportClause | NamespaceImport | ImportSpecifier) => T | undefined): T | undefined { + if (node.name) { + const result = action(node); + if (result) return result; + } + if (node.namedBindings) { + const result = isNamespaceImport(node.namedBindings) + ? action(node.namedBindings) + : forEach(node.namedBindings.elements, action); + if (result) return result; + } + } + export function hasQuestionToken(node: Node) { if (node) { switch (node.kind) { diff --git a/tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValues_error.ts b/tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValues_error.ts index 6a83db2402c01..aa67936654d12 100644 --- a/tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValues_error.ts +++ b/tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValues_error.ts @@ -5,6 +5,7 @@ export default class {} export class A {} export type B = {}; +export const enum C { One, Two } // @Filename: /b.ts import { A, B } from './a'; // Error @@ -26,3 +27,17 @@ console.log(a, b); // @Filename: /e.ts import { A, B } from './a'; // noUnusedLocals error only + +// @Filename: /f.ts +import { C } from './a'; +import type { C as D } from './a'; +C.One; +let c: D = C.Two; +let d: D.Two = C.Two; +console.log(c, d); + +// @Filename: /g.ts +import { C } from './a'; +let c: C; +let d: C.Two; +console.log(c, d); \ No newline at end of file From 124dcd6937c17096082aacdc1a91320a50991fad Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 9 Jan 2020 09:25:32 -0800 Subject: [PATCH 08/24] Update baselines --- .../importsNotUsedAsValues_error.errors.txt | 19 +++++- .../reference/importsNotUsedAsValues_error.js | 29 +++++++++ .../importsNotUsedAsValues_error.symbols | 60 +++++++++++++++++++ .../importsNotUsedAsValues_error.types | 58 ++++++++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/importsNotUsedAsValues_error.errors.txt b/tests/baselines/reference/importsNotUsedAsValues_error.errors.txt index 591a8c643bf68..8fd9453361765 100644 --- a/tests/baselines/reference/importsNotUsedAsValues_error.errors.txt +++ b/tests/baselines/reference/importsNotUsedAsValues_error.errors.txt @@ -1,12 +1,14 @@ /b.ts(1,1): error TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'. /c.ts(1,1): error TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'. /e.ts(1,1): error TS6192: All imports in import declaration are unused. +/g.ts(1,1): error TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'. ==== /a.ts (0 errors) ==== export default class {} export class A {} export type B = {}; + export const enum C { One, Two } ==== /b.ts (1 errors) ==== import { A, B } from './a'; // Error @@ -34,4 +36,19 @@ import { A, B } from './a'; // noUnusedLocals error only ~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS6192: All imports in import declaration are unused. - \ No newline at end of file + +==== /f.ts (0 errors) ==== + import { C } from './a'; + import type { C as D } from './a'; + C.One; + let c: D = C.Two; + let d: D.Two = C.Two; + console.log(c, d); + +==== /g.ts (1 errors) ==== + import { C } from './a'; + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'. + let c: C; + let d: C.Two; + console.log(c, d); \ No newline at end of file diff --git a/tests/baselines/reference/importsNotUsedAsValues_error.js b/tests/baselines/reference/importsNotUsedAsValues_error.js index 124925eaeda53..57f5312f0e3af 100644 --- a/tests/baselines/reference/importsNotUsedAsValues_error.js +++ b/tests/baselines/reference/importsNotUsedAsValues_error.js @@ -4,6 +4,7 @@ export default class {} export class A {} export type B = {}; +export const enum C { One, Two } //// [b.ts] import { A, B } from './a'; // Error @@ -26,6 +27,19 @@ console.log(a, b); //// [e.ts] import { A, B } from './a'; // noUnusedLocals error only +//// [f.ts] +import { C } from './a'; +import type { C as D } from './a'; +C.One; +let c: D = C.Two; +let d: D.Two = C.Two; +console.log(c, d); + +//// [g.ts] +import { C } from './a'; +let c: C; +let d: C.Two; +console.log(c, d); //// [a.js] "use strict"; @@ -67,3 +81,18 @@ console.log(a, b); "use strict"; exports.__esModule = true; require("./a"); // noUnusedLocals error only +//// [f.js] +"use strict"; +exports.__esModule = true; +require("./a"); +0 /* One */; +var c = 1 /* Two */; +var d = 1 /* Two */; +console.log(c, d); +//// [g.js] +"use strict"; +exports.__esModule = true; +require("./a"); +var c; +var d; +console.log(c, d); diff --git a/tests/baselines/reference/importsNotUsedAsValues_error.symbols b/tests/baselines/reference/importsNotUsedAsValues_error.symbols index 0726f61086fa5..bc6b9ec5be807 100644 --- a/tests/baselines/reference/importsNotUsedAsValues_error.symbols +++ b/tests/baselines/reference/importsNotUsedAsValues_error.symbols @@ -6,6 +6,11 @@ export class A {} export type B = {}; >B : Symbol(B, Decl(a.ts, 1, 17)) +export const enum C { One, Two } +>C : Symbol(C, Decl(a.ts, 2, 19)) +>One : Symbol(C.One, Decl(a.ts, 3, 21)) +>Two : Symbol(C.Two, Decl(a.ts, 3, 26)) + === /b.ts === import { A, B } from './a'; // Error >A : Symbol(A, Decl(b.ts, 0, 8)) @@ -72,3 +77,58 @@ import { A, B } from './a'; // noUnusedLocals error only >A : Symbol(A, Decl(e.ts, 0, 8)) >B : Symbol(B, Decl(e.ts, 0, 11)) +=== /f.ts === +import { C } from './a'; +>C : Symbol(C, Decl(f.ts, 0, 8)) + +import type { C as D } from './a'; +>C : Symbol(C, Decl(a.ts, 2, 19)) +>D : Symbol(D, Decl(f.ts, 1, 13)) + +C.One; +>C.One : Symbol(C.One, Decl(a.ts, 3, 21)) +>C : Symbol(C, Decl(f.ts, 0, 8)) +>One : Symbol(C.One, Decl(a.ts, 3, 21)) + +let c: D = C.Two; +>c : Symbol(c, Decl(f.ts, 3, 3)) +>D : Symbol(D, Decl(f.ts, 1, 13)) +>C.Two : Symbol(C.Two, Decl(a.ts, 3, 26)) +>C : Symbol(C, Decl(f.ts, 0, 8)) +>Two : Symbol(C.Two, Decl(a.ts, 3, 26)) + +let d: D.Two = C.Two; +>d : Symbol(d, Decl(f.ts, 4, 3)) +>D : Symbol(D, Decl(f.ts, 1, 13)) +>Two : Symbol(C.Two, Decl(a.ts, 3, 26)) +>C.Two : Symbol(C.Two, Decl(a.ts, 3, 26)) +>C : Symbol(C, Decl(f.ts, 0, 8)) +>Two : Symbol(C.Two, Decl(a.ts, 3, 26)) + +console.log(c, d); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>c : Symbol(c, Decl(f.ts, 3, 3)) +>d : Symbol(d, Decl(f.ts, 4, 3)) + +=== /g.ts === +import { C } from './a'; +>C : Symbol(C, Decl(g.ts, 0, 8)) + +let c: C; +>c : Symbol(c, Decl(g.ts, 1, 3)) +>C : Symbol(C, Decl(g.ts, 0, 8)) + +let d: C.Two; +>d : Symbol(d, Decl(g.ts, 2, 3)) +>C : Symbol(C, Decl(g.ts, 0, 8)) +>Two : Symbol(C.Two, Decl(a.ts, 3, 26)) + +console.log(c, d); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>c : Symbol(c, Decl(g.ts, 1, 3)) +>d : Symbol(d, Decl(g.ts, 2, 3)) + diff --git a/tests/baselines/reference/importsNotUsedAsValues_error.types b/tests/baselines/reference/importsNotUsedAsValues_error.types index 5b166c47e684e..692541840ec16 100644 --- a/tests/baselines/reference/importsNotUsedAsValues_error.types +++ b/tests/baselines/reference/importsNotUsedAsValues_error.types @@ -6,6 +6,11 @@ export class A {} export type B = {}; >B : B +export const enum C { One, Two } +>C : C +>One : C.One +>Two : C.Two + === /b.ts === import { A, B } from './a'; // Error >A : typeof A @@ -70,3 +75,56 @@ import { A, B } from './a'; // noUnusedLocals error only >A : typeof A >B : any +=== /f.ts === +import { C } from './a'; +>C : typeof C + +import type { C as D } from './a'; +>C : typeof C +>D : C + +C.One; +>C.One : C.One +>C : typeof C +>One : C.One + +let c: D = C.Two; +>c : C +>C.Two : C.Two +>C : typeof C +>Two : C.Two + +let d: D.Two = C.Two; +>d : C.Two +>D : any +>C.Two : C.Two +>C : typeof C +>Two : C.Two + +console.log(c, d); +>console.log(c, d) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>c : C.Two +>d : C.Two + +=== /g.ts === +import { C } from './a'; +>C : typeof C + +let c: C; +>c : C + +let d: C.Two; +>d : C.Two +>C : any + +console.log(c, d); +>console.log(c, d) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>c : C +>d : C.Two + From 2dd3690c25edee0a82de3a62137ab05f5d9ca87c Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 10 Jan 2020 14:08:10 -0800 Subject: [PATCH 09/24] Perf tuning 1 --- src/compiler/checker.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c4d6afe495000..3aa4a476fd55e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2476,22 +2476,20 @@ namespace ts { /** Indicates that a symbol directly or indirectly resolves to a type-only import or export. */ function getTypeOnlyAliasDeclaration(symbol: Symbol): Identifier | ImportSpecifier | ExportSpecifier | undefined { + if (!(symbol.flags & SymbolFlags.Alias)) { + return undefined; + } const links = getSymbolLinks(symbol); if (links.typeOnlyDeclaration === undefined) { - if (symbol.flags & SymbolFlags.Alias) { - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - if (isTypeOnlyImportOrExportName(node)) { - links.typeOnlyDeclaration = node; - } - else { - const alias = getImmediateAliasedSymbol(symbol); - links.typeOnlyDeclaration = false; // Prevents circular resolution possible in error scenarios - links.typeOnlyDeclaration = !!alias && getTypeOnlyAliasDeclaration(alias); - } + const node = getDeclarationOfAliasSymbol(symbol); + if (!node) return Debug.fail(); + if (isTypeOnlyImportOrExportName(node)) { + links.typeOnlyDeclaration = node; } else { - links.typeOnlyDeclaration = false; + const alias = getImmediateAliasedSymbol(symbol); + links.typeOnlyDeclaration = false; // Prevents circular resolution possible in error scenarios + links.typeOnlyDeclaration = !!alias && getTypeOnlyAliasDeclaration(alias); } } return links.typeOnlyDeclaration || undefined; From eefa335261438c74ad73f8cf8d227d5d2c233672 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 10 Jan 2020 15:42:03 -0800 Subject: [PATCH 10/24] Test commit for perf impact --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3aa4a476fd55e..ab8f5b7fa3b32 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1852,7 +1852,7 @@ namespace ts { } } if (result && errorLocation && meaning & SymbolFlags.Value && !(errorLocation.flags & NodeFlags.Ambient) && isInExpressionContext(errorLocation)) { - const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result); + const typeOnlyDeclaration = undefined as any; if (typeOnlyDeclaration) { const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type From 9a24cba38c28857a6621030334152faa9a1585d9 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 13 Jan 2020 15:16:27 -0800 Subject: [PATCH 11/24] Weave type-only alias declaration finding into alias resolution --- src/compiler/checker.ts | 69 +++++++++++++++++++++------------ src/compiler/types.ts | 3 +- src/compiler/utilities.ts | 10 +++++ src/compiler/utilitiesPublic.ts | 7 +--- 4 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ab8f5b7fa3b32..9f1b1e619b063 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1852,7 +1852,7 @@ namespace ts { } } if (result && errorLocation && meaning & SymbolFlags.Value && !(errorLocation.flags & NodeFlags.Ambient) && isInExpressionContext(errorLocation)) { - const typeOnlyDeclaration = undefined as any; + const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result); if (typeOnlyDeclaration) { const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type @@ -2149,11 +2149,14 @@ namespace ts { return getSymbolOfPartOfRightHandSideOfImportEquals(node.moduleReference, dontResolveAlias); } - function resolveExportByName(moduleSymbol: Symbol, name: __String, dontResolveAlias: boolean) { + function resolveExportByName(moduleSymbol: Symbol, name: __String, sourceNode: TypeOnlyCompatibleAliasDeclaration | undefined, dontResolveAlias: boolean) { const exportValue = moduleSymbol.exports!.get(InternalSymbolName.ExportEquals); - return exportValue - ? getPropertyOfType(getTypeOfSymbol(exportValue), name) - : resolveSymbol(moduleSymbol.exports!.get(name), dontResolveAlias); + if (exportValue) { + return getPropertyOfType(getTypeOfSymbol(exportValue), name); + } + const exportSymbol = moduleSymbol.exports!.get(name); + checkImportOrExportSymbolForTypeOnlyMarker(sourceNode, exportSymbol); + return resolveSymbol(exportSymbol, dontResolveAlias); } function isSyntacticDefault(node: Node) { @@ -2167,13 +2170,13 @@ namespace ts { // Declaration files (and ambient modules) if (!file || file.isDeclarationFile) { // Definitely cannot have a synthetic default if they have a syntactic default member specified - const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, /*dontResolveAlias*/ true); // Dont resolve alias because we want the immediately exported symbol's declaration + const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, /*sourceNode*/ undefined, /*dontResolveAlias*/ true); // Dont resolve alias because we want the immediately exported symbol's declaration if (defaultExportSymbol && some(defaultExportSymbol.declarations, isSyntacticDefault)) { return false; } // It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member // So we check a bit more, - if (resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), dontResolveAlias)) { + if (resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), /*sourceNode*/ undefined, dontResolveAlias)) { // If there is an `__esModule` specified in the declaration (meaning someone explicitly added it or wrote it in their code), // it definitely is a module and does not have a synthetic default return false; @@ -2188,11 +2191,12 @@ namespace ts { return hasExportAssignmentSymbol(moduleSymbol); } // JS files have a synthetic default if they do not contain ES2015+ module syntax (export = is not valid in js) _and_ do not have an __esModule marker - return !file.externalModuleIndicator && !resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), dontResolveAlias); + return !file.externalModuleIndicator && !resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), /*sourceNode*/ undefined, dontResolveAlias); } function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol | undefined { const moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier); + checkAliasDeclarationForTypeOnlyMarker(node); if (moduleSymbol) { let exportDefaultSymbol: Symbol | undefined; @@ -2200,7 +2204,7 @@ namespace ts { exportDefaultSymbol = moduleSymbol; } else { - exportDefaultSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, dontResolveAlias); + exportDefaultSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, node, dontResolveAlias); } const file = find(moduleSymbol.declarations, isSourceFile); @@ -2243,6 +2247,7 @@ namespace ts { function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol | undefined { const moduleSpecifier = node.parent.parent.moduleSpecifier; + checkAliasDeclarationForTypeOnlyMarker(node); return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false); } @@ -2285,9 +2290,12 @@ namespace ts { return result; } - function getExportOfModule(symbol: Symbol, name: __String, dontResolveAlias: boolean): Symbol | undefined { + function getExportOfModule(symbol: Symbol, specifier: ImportOrExportSpecifier, dontResolveAlias: boolean): Symbol | undefined { if (symbol.flags & SymbolFlags.Module) { - return resolveSymbol(getExportsOfSymbol(symbol).get(name)!, dontResolveAlias); + const name = (specifier.propertyName ?? specifier.name).escapedText; + const exportSymbol = getExportsOfSymbol(symbol).get(name); + checkImportOrExportSymbolForTypeOnlyMarker(specifier, exportSymbol); + return resolveSymbol(exportSymbol, dontResolveAlias); } } @@ -2321,7 +2329,7 @@ namespace ts { } // if symbolFromVariable is export - get its final target symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias); - let symbolFromModule = getExportOfModule(targetSymbol, name.escapedText, dontResolveAlias); + let symbolFromModule = getExportOfModule(targetSymbol, specifier, dontResolveAlias); // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default if (!symbolFromModule && allowSyntheticDefaultImports && name.escapedText === InternalSymbolName.Default) { symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); @@ -2362,6 +2370,7 @@ namespace ts { } function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol | undefined { + checkAliasDeclarationForTypeOnlyMarker(node); return getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias); } @@ -2370,6 +2379,7 @@ namespace ts { } function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) { + checkAliasDeclarationForTypeOnlyMarker(node); return node.parent.parent.moduleSpecifier ? getExternalModuleMember(node.parent.parent, node, dontResolveAlias) : resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias); @@ -2474,24 +2484,30 @@ namespace ts { return links.target; } + function checkImportOrExportSymbolForTypeOnlyMarker(sourceNode: TypeOnlyCompatibleAliasDeclaration | undefined, symbol: Symbol | undefined) { + if (!sourceNode || !symbol) return; + const sourceSymbol = getSymbolOfNode(sourceNode); + const links = getSymbolLinks(sourceSymbol); + if (links.typeOnlyDeclaration === undefined) { + const typeOnly = find(symbol.declarations, isTypeOnlyImportOrExportDeclaration); + links.typeOnlyDeclaration = typeOnly ?? getSymbolLinks(symbol).typeOnlyDeclaration ?? false; + } + } + + function checkAliasDeclarationForTypeOnlyMarker(node: TypeOnlyCompatibleAliasDeclaration) { + if (isTypeOnlyImportOrExportDeclaration(node)) { + const symbol = getSymbolOfNode(node); + const links = getSymbolLinks(symbol); + links.typeOnlyDeclaration = node; + } + } + /** Indicates that a symbol directly or indirectly resolves to a type-only import or export. */ - function getTypeOnlyAliasDeclaration(symbol: Symbol): Identifier | ImportSpecifier | ExportSpecifier | undefined { + function getTypeOnlyAliasDeclaration(symbol: Symbol): TypeOnlyCompatibleAliasDeclaration | undefined { if (!(symbol.flags & SymbolFlags.Alias)) { return undefined; } const links = getSymbolLinks(symbol); - if (links.typeOnlyDeclaration === undefined) { - const node = getDeclarationOfAliasSymbol(symbol); - if (!node) return Debug.fail(); - if (isTypeOnlyImportOrExportName(node)) { - links.typeOnlyDeclaration = node; - } - else { - const alias = getImmediateAliasedSymbol(symbol); - links.typeOnlyDeclaration = false; // Prevents circular resolution possible in error scenarios - links.typeOnlyDeclaration = !!alias && getTypeOnlyAliasDeclaration(alias); - } - } return links.typeOnlyDeclaration || undefined; } @@ -2621,6 +2637,9 @@ namespace ts { throw Debug.assertNever(name, "Unknown entity name kind."); } Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); + if (isIdentifier(name)) { + checkImportOrExportSymbolForTypeOnlyMarker(getTypeOnlyCompatibleAliasDeclarationFromName(name), symbol); + } return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4015253d39914..70c1468d6d6c5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2538,6 +2538,7 @@ namespace ts { } export type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier; + export type TypeOnlyCompatibleAliasDeclaration = ImportClause | NamespaceImport | ImportOrExportSpecifier; /** * This is either an `export =` or an `export default` declaration. @@ -4083,7 +4084,7 @@ namespace ts { deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type deferralParent?: Type; // Source union/intersection of a deferred type cjsExportMerged?: Symbol; // Version of the symbol with all non export= exports merged with the export= target - typeOnlyDeclaration?: Identifier | ImportSpecifier | ExportSpecifier | false; // First resolved alias declaration that makes the symbol only usable in type constructs + typeOnlyDeclaration?: TypeOnlyCompatibleAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3c19ae11d9d93..ed6213ee88d6d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2735,6 +2735,16 @@ namespace ts { node.kind === SyntaxKind.PropertyAssignment && isAliasableExpression((node as PropertyAssignment).initializer); } + export function getTypeOnlyCompatibleAliasDeclarationFromName(node: Identifier): TypeOnlyCompatibleAliasDeclaration | undefined { + switch (node.parent.kind) { + case SyntaxKind.ImportClause: + case SyntaxKind.ImportSpecifier: + case SyntaxKind.NamespaceImport: + case SyntaxKind.ExportSpecifier: + return node.parent as TypeOnlyCompatibleAliasDeclaration; + } + } + function isAliasableExpression(e: Expression) { return isEntityNameExpression(e) || isClassExpression(e); } diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 34507363a39f7..cc36af32f2c77 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1721,14 +1721,11 @@ namespace ts { return isImportSpecifier(node) || isExportSpecifier(node); } - export function isTypeOnlyImportOrExportName(node: Node): node is Identifier | ImportSpecifier | ExportSpecifier { + export function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyCompatibleAliasDeclaration { switch (node.kind) { - case SyntaxKind.Identifier: - return isImportClause(node.parent) // default import name - || isNamespaceImport(node.parent) && node.parent.parent.isTypeOnly; // namespace import name case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - return (node as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; + return (node as ImportOrExportSpecifier).parent.parent.isTypeOnly; case SyntaxKind.NamespaceImport: return (node as NamespaceImport).parent.isTypeOnly; case SyntaxKind.ImportClause: From 875349dbe02430d39db5480896a9f9ae7b0a515c Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 13 Jan 2020 16:22:19 -0800 Subject: [PATCH 12/24] Fix namespace import of type-only exported symbols --- src/compiler/checker.ts | 39 ++++++++----- tests/baselines/reference/chained2.errors.txt | 38 +++++++++++++ tests/baselines/reference/chained2.js | 47 ++++++++++++++++ tests/baselines/reference/chained2.symbols | 53 ++++++++++++++++++ tests/baselines/reference/chained2.types | 55 +++++++++++++++++++ .../externalModules/typeOnly/chained2.ts | 19 +++++++ 6 files changed, 237 insertions(+), 14 deletions(-) create mode 100644 tests/baselines/reference/chained2.errors.txt create mode 100644 tests/baselines/reference/chained2.js create mode 100644 tests/baselines/reference/chained2.symbols create mode 100644 tests/baselines/reference/chained2.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/chained2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9f1b1e619b063..924ca9f3ea81c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1851,25 +1851,33 @@ namespace ts { error(errorLocation, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializer.name), declarationNameToString(errorLocation)); } } - if (result && errorLocation && meaning & SymbolFlags.Value && !(errorLocation.flags & NodeFlags.Ambient) && isInExpressionContext(errorLocation)) { - const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result); - if (typeOnlyDeclaration) { - const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier - ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type - : Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type; - const relatedMessage = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier - ? Diagnostics._0_was_exported_here - : Diagnostics._0_was_imported_here; - const unescapedName = unescapeLeadingUnderscores(name); - addRelatedInfo( - error(errorLocation, message, unescapedName), - createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, unescapedName)); - } + if (result && errorLocation && meaning & SymbolFlags.Value) { + checkSymbolUsageInExpressionContext(result, name, errorLocation); } } return result; } + + + function checkSymbolUsageInExpressionContext(symbol: Symbol, name: __String, useSite: Node, skipContextCheck?: boolean) { + if (!(useSite.flags & NodeFlags.Ambient) && (skipContextCheck || isInExpressionContext(useSite))) { + const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol); + if (typeOnlyDeclaration) { + const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier + ? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type + : Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type; + const relatedMessage = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier + ? Diagnostics._0_was_exported_here + : Diagnostics._0_was_imported_here; + const unescapedName = unescapeLeadingUnderscores(name); + addRelatedInfo( + error(useSite, message, unescapedName), + createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, unescapedName)); + } + } + } + function getIsDeferredContext(location: Node, lastLocation: Node | undefined): boolean { if (location.kind !== SyntaxKind.ArrowFunction && location.kind !== SyntaxKind.FunctionExpression) { // initializers in instance property declaration of class like entities are executed in constructor and thus deferred @@ -23230,6 +23238,9 @@ namespace ts { if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) { markAliasReferenced(parentSymbol, node); } + if (prop && parentSymbol && parentSymbol.flags & SymbolFlags.Alias) { + checkSymbolUsageInExpressionContext(prop, right.escapedText, right, /*skipContextCheck*/ true); + } let propType: Type; if (!prop) { diff --git a/tests/baselines/reference/chained2.errors.txt b/tests/baselines/reference/chained2.errors.txt new file mode 100644 index 0000000000000..adfc8a7b09e0c --- /dev/null +++ b/tests/baselines/reference/chained2.errors.txt @@ -0,0 +1,38 @@ +/d.ts(2,11): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +/d.ts(3,11): error TS1361: 'B' cannot be used as a value because it was imported using 'import type'. +/d.ts(4,7): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. +/d.ts(5,7): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. + + +==== /a.ts (0 errors) ==== + class A { a!: string } + export type { A as default }; + +==== /b.ts (0 errors) ==== + import A from './a'; + import type { default as B } from './a'; + export { A, B }; + +==== /c.ts (0 errors) ==== + import * as types from './b'; + export { types as default }; + +==== /d.ts (4 errors) ==== + import types from './c'; + new types.A(); + ~ +!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. +!!! related TS1377 /a.ts:2:15: 'A' was exported here. + new types.B(); + ~ +!!! error TS1361: 'B' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 /b.ts:2:15: 'B' was imported here. + const a: types.A = {}; + ~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. +!!! related TS2728 /a.ts:1:11: 'a' is declared here. + const b: types.B = {}; + ~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. +!!! related TS2728 /a.ts:1:11: 'a' is declared here. + \ No newline at end of file diff --git a/tests/baselines/reference/chained2.js b/tests/baselines/reference/chained2.js new file mode 100644 index 0000000000000..4eea4824fdd09 --- /dev/null +++ b/tests/baselines/reference/chained2.js @@ -0,0 +1,47 @@ +//// [tests/cases/conformance/externalModules/typeOnly/chained2.ts] //// + +//// [a.ts] +class A { a!: string } +export type { A as default }; + +//// [b.ts] +import A from './a'; +import type { default as B } from './a'; +export { A, B }; + +//// [c.ts] +import * as types from './b'; +export { types as default }; + +//// [d.ts] +import types from './c'; +new types.A(); +new types.B(); +const a: types.A = {}; +const b: types.B = {}; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +//// [b.js] +"use strict"; +exports.__esModule = true; +//// [c.js] +"use strict"; +exports.__esModule = true; +var types = require("./b"); +exports["default"] = types; +//// [d.js] +"use strict"; +exports.__esModule = true; +var c_1 = require("./c"); +new c_1["default"].A(); +new c_1["default"].B(); +var a = {}; +var b = {}; diff --git a/tests/baselines/reference/chained2.symbols b/tests/baselines/reference/chained2.symbols new file mode 100644 index 0000000000000..f686b70333435 --- /dev/null +++ b/tests/baselines/reference/chained2.symbols @@ -0,0 +1,53 @@ +=== /a.ts === +class A { a!: string } +>A : Symbol(A, Decl(a.ts, 0, 0)) +>a : Symbol(A.a, Decl(a.ts, 0, 9)) + +export type { A as default }; +>A : Symbol(A, Decl(a.ts, 0, 0)) +>default : Symbol(default, Decl(a.ts, 1, 13)) + +=== /b.ts === +import A from './a'; +>A : Symbol(A, Decl(b.ts, 0, 6)) + +import type { default as B } from './a'; +>default : Symbol(default, Decl(a.ts, 1, 13)) +>B : Symbol(B, Decl(b.ts, 1, 13)) + +export { A, B }; +>A : Symbol(A, Decl(b.ts, 2, 8)) +>B : Symbol(B, Decl(b.ts, 2, 11)) + +=== /c.ts === +import * as types from './b'; +>types : Symbol(types, Decl(c.ts, 0, 6)) + +export { types as default }; +>types : Symbol(types, Decl(c.ts, 0, 6)) +>default : Symbol(default, Decl(c.ts, 1, 8)) + +=== /d.ts === +import types from './c'; +>types : Symbol(types, Decl(d.ts, 0, 6)) + +new types.A(); +>types.A : Symbol(types.A, Decl(b.ts, 2, 8)) +>types : Symbol(types, Decl(d.ts, 0, 6)) +>A : Symbol(types.A, Decl(b.ts, 2, 8)) + +new types.B(); +>types.B : Symbol(types.B, Decl(b.ts, 2, 11)) +>types : Symbol(types, Decl(d.ts, 0, 6)) +>B : Symbol(types.B, Decl(b.ts, 2, 11)) + +const a: types.A = {}; +>a : Symbol(a, Decl(d.ts, 3, 5)) +>types : Symbol(types, Decl(d.ts, 0, 6)) +>A : Symbol(types.A, Decl(b.ts, 2, 8)) + +const b: types.B = {}; +>b : Symbol(b, Decl(d.ts, 4, 5)) +>types : Symbol(types, Decl(d.ts, 0, 6)) +>B : Symbol(types.B, Decl(b.ts, 2, 11)) + diff --git a/tests/baselines/reference/chained2.types b/tests/baselines/reference/chained2.types new file mode 100644 index 0000000000000..077d8b5be0d9a --- /dev/null +++ b/tests/baselines/reference/chained2.types @@ -0,0 +1,55 @@ +=== /a.ts === +class A { a!: string } +>A : A +>a : string + +export type { A as default }; +>A : typeof A +>default : A + +=== /b.ts === +import A from './a'; +>A : typeof A + +import type { default as B } from './a'; +>default : typeof A +>B : A + +export { A, B }; +>A : typeof A +>B : typeof A + +=== /c.ts === +import * as types from './b'; +>types : typeof types + +export { types as default }; +>types : typeof types +>default : typeof types + +=== /d.ts === +import types from './c'; +>types : typeof types + +new types.A(); +>new types.A() : types.A +>types.A : typeof types.A +>types : typeof types +>A : typeof types.A + +new types.B(); +>new types.B() : types.A +>types.B : typeof types.A +>types : typeof types +>B : typeof types.A + +const a: types.A = {}; +>a : types.A +>types : any +>{} : {} + +const b: types.B = {}; +>b : types.A +>types : any +>{} : {} + diff --git a/tests/cases/conformance/externalModules/typeOnly/chained2.ts b/tests/cases/conformance/externalModules/typeOnly/chained2.ts new file mode 100644 index 0000000000000..1856fa91e5237 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/chained2.ts @@ -0,0 +1,19 @@ +// @Filename: /a.ts +class A { a!: string } +export type { A as default }; + +// @Filename: /b.ts +import A from './a'; +import type { default as B } from './a'; +export { A, B }; + +// @Filename: /c.ts +import * as types from './b'; +export { types as default }; + +// @Filename: /d.ts +import types from './c'; +new types.A(); +new types.B(); +const a: types.A = {}; +const b: types.B = {}; From 171e3141b96528301d71ba4ee59c6ed62eec7ce1 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 13 Jan 2020 16:58:14 -0800 Subject: [PATCH 13/24] type-only exports do not contribute to the module object type --- src/compiler/checker.ts | 5 +--- tests/baselines/reference/chained2.errors.txt | 10 +++----- tests/baselines/reference/chained2.symbols | 4 --- tests/baselines/reference/chained2.types | 12 ++++----- .../namespaceMemberAccess.errors.txt | 16 ++++++++++++ .../reference/namespaceMemberAccess.js | 25 +++++++++++++++++++ .../reference/namespaceMemberAccess.symbols | 19 ++++++++++++++ .../reference/namespaceMemberAccess.types | 21 ++++++++++++++++ .../typeOnly/namespaceMemberAccess.ts | 8 ++++++ 9 files changed, 100 insertions(+), 20 deletions(-) create mode 100644 tests/baselines/reference/namespaceMemberAccess.errors.txt create mode 100644 tests/baselines/reference/namespaceMemberAccess.js create mode 100644 tests/baselines/reference/namespaceMemberAccess.symbols create mode 100644 tests/baselines/reference/namespaceMemberAccess.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/namespaceMemberAccess.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 924ca9f3ea81c..626aa34096073 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3210,7 +3210,7 @@ namespace ts { } function symbolIsValue(symbol: Symbol): boolean { - return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value); + return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value && !getTypeOnlyAliasDeclaration(symbol)); } function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined { @@ -23238,9 +23238,6 @@ namespace ts { if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) { markAliasReferenced(parentSymbol, node); } - if (prop && parentSymbol && parentSymbol.flags & SymbolFlags.Alias) { - checkSymbolUsageInExpressionContext(prop, right.escapedText, right, /*skipContextCheck*/ true); - } let propType: Type; if (!prop) { diff --git a/tests/baselines/reference/chained2.errors.txt b/tests/baselines/reference/chained2.errors.txt index adfc8a7b09e0c..64f61624db8e8 100644 --- a/tests/baselines/reference/chained2.errors.txt +++ b/tests/baselines/reference/chained2.errors.txt @@ -1,5 +1,5 @@ -/d.ts(2,11): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. -/d.ts(3,11): error TS1361: 'B' cannot be used as a value because it was imported using 'import type'. +/d.ts(2,11): error TS2339: Property 'A' does not exist on type 'typeof import("/b")'. +/d.ts(3,11): error TS2339: Property 'B' does not exist on type 'typeof import("/b")'. /d.ts(4,7): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. /d.ts(5,7): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. @@ -21,12 +21,10 @@ import types from './c'; new types.A(); ~ -!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'. -!!! related TS1377 /a.ts:2:15: 'A' was exported here. +!!! error TS2339: Property 'A' does not exist on type 'typeof import("/b")'. new types.B(); ~ -!!! error TS1361: 'B' cannot be used as a value because it was imported using 'import type'. -!!! related TS1376 /b.ts:2:15: 'B' was imported here. +!!! error TS2339: Property 'B' does not exist on type 'typeof import("/b")'. const a: types.A = {}; ~ !!! error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. diff --git a/tests/baselines/reference/chained2.symbols b/tests/baselines/reference/chained2.symbols index f686b70333435..d4cf1c245ae8d 100644 --- a/tests/baselines/reference/chained2.symbols +++ b/tests/baselines/reference/chained2.symbols @@ -32,14 +32,10 @@ import types from './c'; >types : Symbol(types, Decl(d.ts, 0, 6)) new types.A(); ->types.A : Symbol(types.A, Decl(b.ts, 2, 8)) >types : Symbol(types, Decl(d.ts, 0, 6)) ->A : Symbol(types.A, Decl(b.ts, 2, 8)) new types.B(); ->types.B : Symbol(types.B, Decl(b.ts, 2, 11)) >types : Symbol(types, Decl(d.ts, 0, 6)) ->B : Symbol(types.B, Decl(b.ts, 2, 11)) const a: types.A = {}; >a : Symbol(a, Decl(d.ts, 3, 5)) diff --git a/tests/baselines/reference/chained2.types b/tests/baselines/reference/chained2.types index 077d8b5be0d9a..d3b12f0f25c8f 100644 --- a/tests/baselines/reference/chained2.types +++ b/tests/baselines/reference/chained2.types @@ -32,16 +32,16 @@ import types from './c'; >types : typeof types new types.A(); ->new types.A() : types.A ->types.A : typeof types.A +>new types.A() : any +>types.A : any >types : typeof types ->A : typeof types.A +>A : any new types.B(); ->new types.B() : types.A ->types.B : typeof types.A +>new types.B() : any +>types.B : any >types : typeof types ->B : typeof types.A +>B : any const a: types.A = {}; >a : types.A diff --git a/tests/baselines/reference/namespaceMemberAccess.errors.txt b/tests/baselines/reference/namespaceMemberAccess.errors.txt new file mode 100644 index 0000000000000..c2f85e7941d8a --- /dev/null +++ b/tests/baselines/reference/namespaceMemberAccess.errors.txt @@ -0,0 +1,16 @@ +/b.ts(2,7): error TS2339: Property 'A' does not exist on type 'typeof import("/a")'. +/b.ts(3,9): error TS2339: Property 'A' does not exist on type 'typeof import("/a")'. + + +==== /a.ts (0 errors) ==== + class A { a!: string } + export type { A }; + +==== /b.ts (2 errors) ==== + import * as types from './a'; + types.A; + ~ +!!! error TS2339: Property 'A' does not exist on type 'typeof import("/a")'. + const { A } = types; + ~ +!!! error TS2339: Property 'A' does not exist on type 'typeof import("/a")'. \ No newline at end of file diff --git a/tests/baselines/reference/namespaceMemberAccess.js b/tests/baselines/reference/namespaceMemberAccess.js new file mode 100644 index 0000000000000..2ab4b6e11fbe6 --- /dev/null +++ b/tests/baselines/reference/namespaceMemberAccess.js @@ -0,0 +1,25 @@ +//// [tests/cases/conformance/externalModules/typeOnly/namespaceMemberAccess.ts] //// + +//// [a.ts] +class A { a!: string } +export type { A }; + +//// [b.ts] +import * as types from './a'; +types.A; +const { A } = types; + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +//// [b.js] +"use strict"; +exports.__esModule = true; +var types = require("./a"); +types.A; +var A = types.A; diff --git a/tests/baselines/reference/namespaceMemberAccess.symbols b/tests/baselines/reference/namespaceMemberAccess.symbols new file mode 100644 index 0000000000000..d9d2bfaf77027 --- /dev/null +++ b/tests/baselines/reference/namespaceMemberAccess.symbols @@ -0,0 +1,19 @@ +=== /a.ts === +class A { a!: string } +>A : Symbol(A, Decl(a.ts, 0, 0)) +>a : Symbol(A.a, Decl(a.ts, 0, 9)) + +export type { A }; +>A : Symbol(A, Decl(a.ts, 1, 13)) + +=== /b.ts === +import * as types from './a'; +>types : Symbol(types, Decl(b.ts, 0, 6)) + +types.A; +>types : Symbol(types, Decl(b.ts, 0, 6)) + +const { A } = types; +>A : Symbol(A, Decl(b.ts, 2, 7)) +>types : Symbol(types, Decl(b.ts, 0, 6)) + diff --git a/tests/baselines/reference/namespaceMemberAccess.types b/tests/baselines/reference/namespaceMemberAccess.types new file mode 100644 index 0000000000000..a86a4c27b742e --- /dev/null +++ b/tests/baselines/reference/namespaceMemberAccess.types @@ -0,0 +1,21 @@ +=== /a.ts === +class A { a!: string } +>A : A +>a : string + +export type { A }; +>A : A + +=== /b.ts === +import * as types from './a'; +>types : typeof types + +types.A; +>types.A : any +>types : typeof types +>A : any + +const { A } = types; +>A : any +>types : typeof types + diff --git a/tests/cases/conformance/externalModules/typeOnly/namespaceMemberAccess.ts b/tests/cases/conformance/externalModules/typeOnly/namespaceMemberAccess.ts new file mode 100644 index 0000000000000..98338e054bd11 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/namespaceMemberAccess.ts @@ -0,0 +1,8 @@ +// @Filename: /a.ts +class A { a!: string } +export type { A }; + +// @Filename: /b.ts +import * as types from './a'; +types.A; +const { A } = types; \ No newline at end of file From 910dd84d36233d83d2c6c623bf66eb76c23c22eb Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Jan 2020 09:00:39 -0800 Subject: [PATCH 14/24] Update APIs --- tests/baselines/reference/api/tsserverlibrary.d.ts | 3 ++- tests/baselines/reference/api/typescript.d.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index a8d10d88f27ad..aa75fbc871daa 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1547,6 +1547,7 @@ declare namespace ts { name: Identifier; } export type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier; + export type TypeOnlyCompatibleAliasDeclaration = ImportClause | NamespaceImport | ImportOrExportSpecifier; /** * This is either an `export =` or an `export default` declaration. * Unless `isExportEquals` is set, this node was parsed as an `export default`. @@ -3739,7 +3740,7 @@ declare namespace ts { function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken; function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail; function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier; - function isTypeOnlyImportOrExportName(node: Node): node is Identifier | ImportSpecifier | ExportSpecifier; + function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyCompatibleAliasDeclaration; function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken; function isModifier(node: Node): node is Modifier; function isEntityName(node: Node): node is EntityName; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 06c3190a266c9..29ae5a89ffb51 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1547,6 +1547,7 @@ declare namespace ts { name: Identifier; } export type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier; + export type TypeOnlyCompatibleAliasDeclaration = ImportClause | NamespaceImport | ImportOrExportSpecifier; /** * This is either an `export =` or an `export default` declaration. * Unless `isExportEquals` is set, this node was parsed as an `export default`. @@ -3739,7 +3740,7 @@ declare namespace ts { function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken; function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail; function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier; - function isTypeOnlyImportOrExportName(node: Node): node is Identifier | ImportSpecifier | ExportSpecifier; + function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyCompatibleAliasDeclaration; function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken; function isModifier(node: Node): node is Modifier; function isEntityName(node: Node): node is EntityName; From 2a7c472b63f7fdc3492ed8273275fbfc144db51a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Jan 2020 10:20:03 -0800 Subject: [PATCH 15/24] Fix enum casing, remove type-only conversion suggestion --- src/compiler/checker.ts | 13 ++++++------- src/compiler/commandLineParser.ts | 6 +++--- src/compiler/diagnosticMessages.json | 4 ---- src/compiler/transformers/ts.ts | 6 +++--- src/compiler/types.ts | 4 ++-- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 626aa34096073..da2cfb4980098 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33101,13 +33101,9 @@ namespace ts { !isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) && !importClauseContainsConstEnumUsedAsValue(statement.importClause) ) { - const isError = compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Error; - errorOrSuggestion( - isError, + error( statement, - isError - ? Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValues_is_set_to_error - : Diagnostics.This_import_may_be_converted_to_a_type_only_import); + Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValues_is_set_to_error); } } } @@ -33604,7 +33600,10 @@ namespace ts { }); } - if (!node.isDeclarationFile && isExternalModule(node)) { + if (compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && + !node.isDeclarationFile && + isExternalModule(node) + ) { checkImportsForTypeOnlyConversion(node); } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 300e403b3717f..77ba8d86161e1 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -475,9 +475,9 @@ namespace ts { { name: "importsNotUsedAsValues", type: createMapFromTemplate({ - remove: importsNotUsedAsValues.Remove, - preserve: importsNotUsedAsValues.Preserve, - error: importsNotUsedAsValues.Error + remove: ImportsNotUsedAsValues.Remove, + preserve: ImportsNotUsedAsValues.Preserve, + error: ImportsNotUsedAsValues.Error }), affectsEmit: true, category: Diagnostics.Advanced_Options, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 63c07aada3d50..9461383fece87 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1103,10 +1103,6 @@ "category": "Error", "code": 1371 }, - "This import may be converted to a type-only import.": { - "category": "Suggestion", - "code": 1372 - }, "Convert to type-only import": { "category": "Message", "code": 1373 diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index bcab043fd25e5..9d7dc9375996b 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2778,8 +2778,8 @@ namespace ts { // Elide the declaration if the import clause was elided. const importClause = visitNode(node.importClause, visitImportClause, isImportClause); return importClause || - compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Preserve || - compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Error + compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve || + compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error ? updateImportDeclaration( node, /*decorators*/ undefined, @@ -2931,7 +2931,7 @@ namespace ts { if (isExternalModuleImportEqualsDeclaration(node)) { const isReferenced = resolver.isReferencedAliasDeclaration(node); // If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'. - if (!isReferenced && compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Preserve) { + if (!isReferenced && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve) { return setOriginalNode( setTextRange( createImportDeclaration( diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 70c1468d6d6c5..3777755e97556 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5046,7 +5046,7 @@ namespace ts { /*@internal*/generateCpuProfile?: string; /*@internal*/help?: boolean; importHelpers?: boolean; - importsNotUsedAsValues?: importsNotUsedAsValues; + importsNotUsedAsValues?: ImportsNotUsedAsValues; /*@internal*/init?: boolean; inlineSourceMap?: boolean; inlineSources?: boolean; @@ -5168,7 +5168,7 @@ namespace ts { ReactNative = 3 } - export const enum importsNotUsedAsValues { + export const enum ImportsNotUsedAsValues { Remove, Preserve, Error From 11ba11a597a6ac765655d8f1c115a904862c0563 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Jan 2020 10:25:06 -0800 Subject: [PATCH 16/24] Short circuit type-only checks in resolveEntityName faster --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index da2cfb4980098..8c335b8f40186 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2645,7 +2645,7 @@ namespace ts { throw Debug.assertNever(name, "Unknown entity name kind."); } Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); - if (isIdentifier(name)) { + if (isIdentifier(name) && symbol.flags & SymbolFlags.Alias) { checkImportOrExportSymbolForTypeOnlyMarker(getTypeOnlyCompatibleAliasDeclarationFromName(name), symbol); } return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol); From 0c28994ebe38aa031bc2c789468ed593ab5da811 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Jan 2020 10:26:15 -0800 Subject: [PATCH 17/24] Fix casing in API --- tests/baselines/reference/api/tsserverlibrary.d.ts | 4 ++-- tests/baselines/reference/api/typescript.d.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index aa75fbc871daa..6c21d387279be 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2652,7 +2652,7 @@ declare namespace ts { experimentalDecorators?: boolean; forceConsistentCasingInFileNames?: boolean; importHelpers?: boolean; - importsNotUsedAsValues?: importsNotUsedAsValues; + importsNotUsedAsValues?: ImportsNotUsedAsValues; inlineSourceMap?: boolean; inlineSources?: boolean; isolatedModules?: boolean; @@ -2751,7 +2751,7 @@ declare namespace ts { React = 2, ReactNative = 3 } - export enum importsNotUsedAsValues { + export enum ImportsNotUsedAsValues { Remove = 0, Preserve = 1, Error = 2 diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 29ae5a89ffb51..15ec1d721702a 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2652,7 +2652,7 @@ declare namespace ts { experimentalDecorators?: boolean; forceConsistentCasingInFileNames?: boolean; importHelpers?: boolean; - importsNotUsedAsValues?: importsNotUsedAsValues; + importsNotUsedAsValues?: ImportsNotUsedAsValues; inlineSourceMap?: boolean; inlineSources?: boolean; isolatedModules?: boolean; @@ -2751,7 +2751,7 @@ declare namespace ts { React = 2, ReactNative = 3 } - export enum importsNotUsedAsValues { + export enum ImportsNotUsedAsValues { Remove = 0, Preserve = 1, Error = 2 From 40a2c3c2108a1d3db9c0865b878de537c3ed3a12 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Jan 2020 10:38:54 -0800 Subject: [PATCH 18/24] Remove unused parameter --- src/compiler/checker.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8c335b8f40186..a1dc1131068c3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1851,17 +1851,15 @@ namespace ts { error(errorLocation, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializer.name), declarationNameToString(errorLocation)); } } - if (result && errorLocation && meaning & SymbolFlags.Value) { + if (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias) { checkSymbolUsageInExpressionContext(result, name, errorLocation); } } return result; } - - - function checkSymbolUsageInExpressionContext(symbol: Symbol, name: __String, useSite: Node, skipContextCheck?: boolean) { - if (!(useSite.flags & NodeFlags.Ambient) && (skipContextCheck || isInExpressionContext(useSite))) { + function checkSymbolUsageInExpressionContext(symbol: Symbol, name: __String, useSite: Node) { + if (!(useSite.flags & NodeFlags.Ambient) && isInExpressionContext(useSite)) { const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol); if (typeOnlyDeclaration) { const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier From 124d7460af10a0efe81335117695711b6fe4ed0d Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 14 Jan 2020 12:56:44 -0800 Subject: [PATCH 19/24] Fix error on qualified names in type queries --- src/compiler/checker.ts | 2 +- src/compiler/utilities.ts | 7 ++++ .../namespaceImportTypeQuery.errors.txt | 15 +++++++++ .../reference/namespaceImportTypeQuery.js | 33 +++++++++++++++++++ .../namespaceImportTypeQuery.symbols | 24 ++++++++++++++ .../reference/namespaceImportTypeQuery.types | 26 +++++++++++++++ .../typeOnly/namespaceImportTypeQuery.ts | 9 +++++ 7 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/namespaceImportTypeQuery.errors.txt create mode 100644 tests/baselines/reference/namespaceImportTypeQuery.js create mode 100644 tests/baselines/reference/namespaceImportTypeQuery.symbols create mode 100644 tests/baselines/reference/namespaceImportTypeQuery.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/namespaceImportTypeQuery.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6478e96f5c0da..254592418e02b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1865,7 +1865,7 @@ namespace ts { } function checkSymbolUsageInExpressionContext(symbol: Symbol, name: __String, useSite: Node) { - if (!(useSite.flags & NodeFlags.Ambient) && isInExpressionContext(useSite)) { + if (!(useSite.flags & NodeFlags.Ambient) && !isPartOfTypeQuery(useSite) && isExpressionNode(useSite)) { const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol); if (typeOnlyDeclaration) { const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 601db3fee64ff..1b31ac46bd52e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1771,6 +1771,13 @@ namespace ts { } } + export function isPartOfTypeQuery(node: Node) { + while (node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.Identifier) { + node = node.parent; + } + return node.kind === SyntaxKind.TypeQuery; + } + export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } { return node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference; } diff --git a/tests/baselines/reference/namespaceImportTypeQuery.errors.txt b/tests/baselines/reference/namespaceImportTypeQuery.errors.txt new file mode 100644 index 0000000000000..5036215385d45 --- /dev/null +++ b/tests/baselines/reference/namespaceImportTypeQuery.errors.txt @@ -0,0 +1,15 @@ +/b.ts(2,21): error TS2339: Property 'A' does not exist on type 'typeof import("/a")'. + + +==== /a.ts (0 errors) ==== + class A {} + export type { A }; + export class B {}; + +==== /b.ts (1 errors) ==== + import * as types from './a'; + let A: typeof types.A; + ~ +!!! error TS2339: Property 'A' does not exist on type 'typeof import("/a")'. + let B: typeof types.B; + \ No newline at end of file diff --git a/tests/baselines/reference/namespaceImportTypeQuery.js b/tests/baselines/reference/namespaceImportTypeQuery.js new file mode 100644 index 0000000000000..2ecc28ad362e4 --- /dev/null +++ b/tests/baselines/reference/namespaceImportTypeQuery.js @@ -0,0 +1,33 @@ +//// [tests/cases/conformance/externalModules/typeOnly/namespaceImportTypeQuery.ts] //// + +//// [a.ts] +class A {} +export type { A }; +export class B {}; + +//// [b.ts] +import * as types from './a'; +let A: typeof types.A; +let B: typeof types.B; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +var B = /** @class */ (function () { + function B() { + } + return B; +}()); +exports.B = B; +; +//// [b.js] +"use strict"; +exports.__esModule = true; +var A; +var B; diff --git a/tests/baselines/reference/namespaceImportTypeQuery.symbols b/tests/baselines/reference/namespaceImportTypeQuery.symbols new file mode 100644 index 0000000000000..9851b31b6b001 --- /dev/null +++ b/tests/baselines/reference/namespaceImportTypeQuery.symbols @@ -0,0 +1,24 @@ +=== /a.ts === +class A {} +>A : Symbol(A, Decl(a.ts, 0, 0)) + +export type { A }; +>A : Symbol(A, Decl(a.ts, 1, 13)) + +export class B {}; +>B : Symbol(B, Decl(a.ts, 1, 18)) + +=== /b.ts === +import * as types from './a'; +>types : Symbol(types, Decl(b.ts, 0, 6)) + +let A: typeof types.A; +>A : Symbol(A, Decl(b.ts, 1, 3)) +>types : Symbol(types, Decl(b.ts, 0, 6)) + +let B: typeof types.B; +>B : Symbol(B, Decl(b.ts, 2, 3)) +>types.B : Symbol(types.B, Decl(a.ts, 1, 18)) +>types : Symbol(types, Decl(b.ts, 0, 6)) +>B : Symbol(types.B, Decl(a.ts, 1, 18)) + diff --git a/tests/baselines/reference/namespaceImportTypeQuery.types b/tests/baselines/reference/namespaceImportTypeQuery.types new file mode 100644 index 0000000000000..273a46b1507bc --- /dev/null +++ b/tests/baselines/reference/namespaceImportTypeQuery.types @@ -0,0 +1,26 @@ +=== /a.ts === +class A {} +>A : A + +export type { A }; +>A : A + +export class B {}; +>B : B + +=== /b.ts === +import * as types from './a'; +>types : typeof types + +let A: typeof types.A; +>A : any +>types.A : any +>types : typeof types +>A : any + +let B: typeof types.B; +>B : typeof types.B +>types.B : typeof types.B +>types : typeof types +>B : typeof types.B + diff --git a/tests/cases/conformance/externalModules/typeOnly/namespaceImportTypeQuery.ts b/tests/cases/conformance/externalModules/typeOnly/namespaceImportTypeQuery.ts new file mode 100644 index 0000000000000..10e8e020158e8 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/namespaceImportTypeQuery.ts @@ -0,0 +1,9 @@ +// @Filename: /a.ts +class A {} +export type { A }; +export class B {}; + +// @Filename: /b.ts +import * as types from './a'; +let A: typeof types.A; +let B: typeof types.B; From 1044c5c4c79de278004168f3980cd07d05943842 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 15 Jan 2020 11:12:10 -0800 Subject: [PATCH 20/24] Allow type-only imports in computed property names --- src/compiler/checker.ts | 7 ++++++- src/compiler/utilities.ts | 7 +++++++ .../baselines/reference/computedPropertyName.js | 16 ++++++++++++++++ .../reference/computedPropertyName.symbols | 17 +++++++++++++++++ .../reference/computedPropertyName.types | 17 +++++++++++++++++ .../typeOnly/computedPropertyName.ts | 11 +++++++++++ 6 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/computedPropertyName.js create mode 100644 tests/baselines/reference/computedPropertyName.symbols create mode 100644 tests/baselines/reference/computedPropertyName.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c1584620b2624..1f268edfde616 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1865,7 +1865,12 @@ namespace ts { } function checkSymbolUsageInExpressionContext(symbol: Symbol, name: __String, useSite: Node) { - if (!(useSite.flags & NodeFlags.Ambient) && !isPartOfTypeQuery(useSite) && isExpressionNode(useSite)) { + if ( + !(useSite.flags & NodeFlags.Ambient) && + !isPartOfTypeQuery(useSite) && + !isPartOfPossiblyValidComputedPropertyNameExpression(useSite) && + isExpressionNode(useSite) + ) { const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol); if (typeOnlyDeclaration) { const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1b31ac46bd52e..0bf10b065f3ad 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1778,6 +1778,13 @@ namespace ts { return node.kind === SyntaxKind.TypeQuery; } + export function isPartOfPossiblyValidComputedPropertyNameExpression(node: Node) { + while (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) { + node = node.parent; + } + return node.kind === SyntaxKind.ComputedPropertyName; + } + export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } { return node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference; } diff --git a/tests/baselines/reference/computedPropertyName.js b/tests/baselines/reference/computedPropertyName.js new file mode 100644 index 0000000000000..7a2bfa2d75681 --- /dev/null +++ b/tests/baselines/reference/computedPropertyName.js @@ -0,0 +1,16 @@ +//// [tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts] //// + +//// [framework-hooks.ts] +export const onInit = Symbol("onInit"); + +//// [component.ts] +import type { onInit } from "./framework-hooks"; + +interface Component { + [onInit]?(): void; +} + + +//// [framework-hooks.js] +export const onInit = Symbol("onInit"); +//// [component.js] diff --git a/tests/baselines/reference/computedPropertyName.symbols b/tests/baselines/reference/computedPropertyName.symbols new file mode 100644 index 0000000000000..b9103524861ab --- /dev/null +++ b/tests/baselines/reference/computedPropertyName.symbols @@ -0,0 +1,17 @@ +=== tests/cases/conformance/externalModules/typeOnly/framework-hooks.ts === +export const onInit = Symbol("onInit"); +>onInit : Symbol(onInit, Decl(framework-hooks.ts, 0, 12)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) + +=== tests/cases/conformance/externalModules/typeOnly/component.ts === +import type { onInit } from "./framework-hooks"; +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) + +interface Component { +>Component : Symbol(Component, Decl(component.ts, 0, 48)) + + [onInit]?(): void; +>[onInit] : Symbol(Component[onInit], Decl(component.ts, 2, 21)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) +} + diff --git a/tests/baselines/reference/computedPropertyName.types b/tests/baselines/reference/computedPropertyName.types new file mode 100644 index 0000000000000..59a092f561bd2 --- /dev/null +++ b/tests/baselines/reference/computedPropertyName.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/externalModules/typeOnly/framework-hooks.ts === +export const onInit = Symbol("onInit"); +>onInit : unique symbol +>Symbol("onInit") : unique symbol +>Symbol : SymbolConstructor +>"onInit" : "onInit" + +=== tests/cases/conformance/externalModules/typeOnly/component.ts === +import type { onInit } from "./framework-hooks"; +>onInit : any + +interface Component { + [onInit]?(): void; +>[onInit] : () => void +>onInit : unique symbol +} + diff --git a/tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts b/tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts new file mode 100644 index 0000000000000..2f5028d0697bc --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts @@ -0,0 +1,11 @@ +// @target: esnext + +// @Filename: framework-hooks.ts +export const onInit = Symbol("onInit"); + +// @Filename: component.ts +import type { onInit } from "./framework-hooks"; + +interface Component { + [onInit]?(): void; +} From a5ca4924fa9b109854168dc9e58c2df5b736a6b8 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 15 Jan 2020 18:01:13 -0800 Subject: [PATCH 21/24] Fix computed property names of types and abstract members --- src/compiler/checker.ts | 2 +- src/compiler/utilities.ts | 15 +++++++-- .../typeOnly/computedPropertyName.ts | 32 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f268edfde616..d1ecca4350754 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1868,7 +1868,7 @@ namespace ts { if ( !(useSite.flags & NodeFlags.Ambient) && !isPartOfTypeQuery(useSite) && - !isPartOfPossiblyValidComputedPropertyNameExpression(useSite) && + !isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) && isExpressionNode(useSite) ) { const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0bf10b065f3ad..2d606ae9aa482 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1778,11 +1778,22 @@ namespace ts { return node.kind === SyntaxKind.TypeQuery; } - export function isPartOfPossiblyValidComputedPropertyNameExpression(node: Node) { + export function isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node: Node) { while (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) { node = node.parent; } - return node.kind === SyntaxKind.ComputedPropertyName; + if (node.kind !== SyntaxKind.ComputedPropertyName) { + return false; + } + if (hasModifier(node.parent, ModifierFlags.Abstract)) { + return true; + } + const containerKind = node.parent.parent.kind; + return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral; + } + + export function isAbstractDeclarationName(node: Node) { + return isDeclarationName(node) && hasModifier(node, ModifierFlags.Abstract); } export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } { diff --git a/tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts b/tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts index 2f5028d0697bc..9f64657a97701 100644 --- a/tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts +++ b/tests/cases/conformance/externalModules/typeOnly/computedPropertyName.ts @@ -9,3 +9,35 @@ import type { onInit } from "./framework-hooks"; interface Component { [onInit]?(): void; } + +type T = { + [onInit]: any; +} + +const o = { + [onInit]: 0 // Error +}; + +class C { + [onInit]: any; // Error (because class fields) +} + +class D { + [onInit] = 0; // Error +} + +class E { + [onInit]() {} // Error +} + +abstract class F { + abstract [onInit](): void; +} + +class G { + declare [onInit]: any; +} + +declare class H { + [onInit]: any; +} From 8571a8ac9dcb404146d6ff5f0a73dfb24b51669e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 15 Jan 2020 18:30:42 -0800 Subject: [PATCH 22/24] Remove unused util --- src/compiler/utilities.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2d606ae9aa482..e71fac157627c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1792,10 +1792,6 @@ namespace ts { return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral; } - export function isAbstractDeclarationName(node: Node) { - return isDeclarationName(node) && hasModifier(node, ModifierFlags.Abstract); - } - export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } { return node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference; } From 8b4c2355df58bcfe0a8df89fc22b45d7ed7ee14b Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 16 Jan 2020 09:41:36 -0800 Subject: [PATCH 23/24] Commit missing baselines --- .../reference/computedPropertyName.errors.txt | 60 ++++++++++++++++ .../reference/computedPropertyName.js | 51 ++++++++++++++ .../reference/computedPropertyName.symbols | 65 ++++++++++++++++++ .../reference/computedPropertyName.types | 68 +++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 tests/baselines/reference/computedPropertyName.errors.txt diff --git a/tests/baselines/reference/computedPropertyName.errors.txt b/tests/baselines/reference/computedPropertyName.errors.txt new file mode 100644 index 0000000000000..5038e0be48422 --- /dev/null +++ b/tests/baselines/reference/computedPropertyName.errors.txt @@ -0,0 +1,60 @@ +tests/cases/conformance/externalModules/typeOnly/component.ts(12,4): error TS1361: 'onInit' cannot be used as a value because it was imported using 'import type'. +tests/cases/conformance/externalModules/typeOnly/component.ts(16,4): error TS1361: 'onInit' cannot be used as a value because it was imported using 'import type'. +tests/cases/conformance/externalModules/typeOnly/component.ts(20,4): error TS1361: 'onInit' cannot be used as a value because it was imported using 'import type'. +tests/cases/conformance/externalModules/typeOnly/component.ts(24,4): error TS1361: 'onInit' cannot be used as a value because it was imported using 'import type'. + + +==== tests/cases/conformance/externalModules/typeOnly/framework-hooks.ts (0 errors) ==== + export const onInit = Symbol("onInit"); + +==== tests/cases/conformance/externalModules/typeOnly/component.ts (4 errors) ==== + import type { onInit } from "./framework-hooks"; + + interface Component { + [onInit]?(): void; + } + + type T = { + [onInit]: any; + } + + const o = { + [onInit]: 0 // Error + ~~~~~~ +!!! error TS1361: 'onInit' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 tests/cases/conformance/externalModules/typeOnly/component.ts:1:15: 'onInit' was imported here. + }; + + class C { + [onInit]: any; // Error (because class fields) + ~~~~~~ +!!! error TS1361: 'onInit' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 tests/cases/conformance/externalModules/typeOnly/component.ts:1:15: 'onInit' was imported here. + } + + class D { + [onInit] = 0; // Error + ~~~~~~ +!!! error TS1361: 'onInit' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 tests/cases/conformance/externalModules/typeOnly/component.ts:1:15: 'onInit' was imported here. + } + + class E { + [onInit]() {} // Error + ~~~~~~ +!!! error TS1361: 'onInit' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 tests/cases/conformance/externalModules/typeOnly/component.ts:1:15: 'onInit' was imported here. + } + + abstract class F { + abstract [onInit](): void; + } + + class G { + declare [onInit]: any; + } + + declare class H { + [onInit]: any; + } + \ No newline at end of file diff --git a/tests/baselines/reference/computedPropertyName.js b/tests/baselines/reference/computedPropertyName.js index 7a2bfa2d75681..a3378327f282d 100644 --- a/tests/baselines/reference/computedPropertyName.js +++ b/tests/baselines/reference/computedPropertyName.js @@ -9,8 +9,59 @@ import type { onInit } from "./framework-hooks"; interface Component { [onInit]?(): void; } + +type T = { + [onInit]: any; +} + +const o = { + [onInit]: 0 // Error +}; + +class C { + [onInit]: any; // Error (because class fields) +} + +class D { + [onInit] = 0; // Error +} + +class E { + [onInit]() {} // Error +} + +abstract class F { + abstract [onInit](): void; +} + +class G { + declare [onInit]: any; +} + +declare class H { + [onInit]: any; +} //// [framework-hooks.js] export const onInit = Symbol("onInit"); //// [component.js] +var _a; +const o = { + [onInit]: 0 // Error +}; +class C { +} +class D { + constructor() { + this[_a] = 0; // Error + } +} +_a = onInit; +class E { + [onInit]() { } // Error +} +class F { +} +class G { +} diff --git a/tests/baselines/reference/computedPropertyName.symbols b/tests/baselines/reference/computedPropertyName.symbols index b9103524861ab..9a8e912fae160 100644 --- a/tests/baselines/reference/computedPropertyName.symbols +++ b/tests/baselines/reference/computedPropertyName.symbols @@ -15,3 +15,68 @@ interface Component { >onInit : Symbol(onInit, Decl(component.ts, 0, 13)) } +type T = { +>T : Symbol(T, Decl(component.ts, 4, 1)) + + [onInit]: any; +>[onInit] : Symbol([onInit], Decl(component.ts, 6, 10)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) +} + +const o = { +>o : Symbol(o, Decl(component.ts, 10, 5)) + + [onInit]: 0 // Error +>[onInit] : Symbol([onInit], Decl(component.ts, 10, 11)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) + +}; + +class C { +>C : Symbol(C, Decl(component.ts, 12, 2)) + + [onInit]: any; // Error (because class fields) +>[onInit] : Symbol(C[onInit], Decl(component.ts, 14, 9)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) +} + +class D { +>D : Symbol(D, Decl(component.ts, 16, 1)) + + [onInit] = 0; // Error +>[onInit] : Symbol(D[onInit], Decl(component.ts, 18, 9)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) +} + +class E { +>E : Symbol(E, Decl(component.ts, 20, 1)) + + [onInit]() {} // Error +>[onInit] : Symbol(E[onInit], Decl(component.ts, 22, 9)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) +} + +abstract class F { +>F : Symbol(F, Decl(component.ts, 24, 1)) + + abstract [onInit](): void; +>[onInit] : Symbol(F[onInit], Decl(component.ts, 26, 18)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) +} + +class G { +>G : Symbol(G, Decl(component.ts, 28, 1)) + + declare [onInit]: any; +>[onInit] : Symbol(G[onInit], Decl(component.ts, 30, 9)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) +} + +declare class H { +>H : Symbol(H, Decl(component.ts, 32, 1)) + + [onInit]: any; +>[onInit] : Symbol(H[onInit], Decl(component.ts, 34, 17)) +>onInit : Symbol(onInit, Decl(component.ts, 0, 13)) +} + diff --git a/tests/baselines/reference/computedPropertyName.types b/tests/baselines/reference/computedPropertyName.types index 59a092f561bd2..caab943f248ec 100644 --- a/tests/baselines/reference/computedPropertyName.types +++ b/tests/baselines/reference/computedPropertyName.types @@ -15,3 +15,71 @@ interface Component { >onInit : unique symbol } +type T = { +>T : T + + [onInit]: any; +>[onInit] : any +>onInit : unique symbol +} + +const o = { +>o : { [onInit]: number; } +>{ [onInit]: 0 // Error} : { [onInit]: number; } + + [onInit]: 0 // Error +>[onInit] : number +>onInit : unique symbol +>0 : 0 + +}; + +class C { +>C : C + + [onInit]: any; // Error (because class fields) +>[onInit] : any +>onInit : unique symbol +} + +class D { +>D : D + + [onInit] = 0; // Error +>[onInit] : number +>onInit : unique symbol +>0 : 0 +} + +class E { +>E : E + + [onInit]() {} // Error +>[onInit] : () => void +>onInit : unique symbol +} + +abstract class F { +>F : F + + abstract [onInit](): void; +>[onInit] : () => void +>onInit : unique symbol +} + +class G { +>G : G + + declare [onInit]: any; +>[onInit] : any +>onInit : unique symbol +} + +declare class H { +>H : H + + [onInit]: any; +>[onInit] : any +>onInit : unique symbol +} + From 19b32066d20f4f76ab93341436e374ca55cdbc02 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 23 Jan 2020 12:26:13 -0800 Subject: [PATCH 24/24] =?UTF-8?q?Rename=20=E2=80=9Ccheck=E2=80=9D=20functi?= =?UTF-8?q?ons=20so=20as=20not=20to=20overload=20the=20word=20=E2=80=9Cche?= =?UTF-8?q?ck=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/compiler/checker.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 03fd821c4d072..508d2dd135a40 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2176,7 +2176,7 @@ namespace ts { return getPropertyOfType(getTypeOfSymbol(exportValue), name); } const exportSymbol = moduleSymbol.exports!.get(name); - checkImportOrExportSymbolForTypeOnlyMarker(sourceNode, exportSymbol); + markSymbolOfAliasDeclarationIfResolvesToTypeOnly(sourceNode, exportSymbol); return resolveSymbol(exportSymbol, dontResolveAlias); } @@ -2217,7 +2217,7 @@ namespace ts { function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol | undefined { const moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier); - checkAliasDeclarationForTypeOnlyMarker(node); + markSymbolOfAliasDeclarationIfTypeOnly(node); if (moduleSymbol) { let exportDefaultSymbol: Symbol | undefined; @@ -2268,7 +2268,7 @@ namespace ts { function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol | undefined { const moduleSpecifier = node.parent.parent.moduleSpecifier; - checkAliasDeclarationForTypeOnlyMarker(node); + markSymbolOfAliasDeclarationIfTypeOnly(node); return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false); } @@ -2315,7 +2315,7 @@ namespace ts { if (symbol.flags & SymbolFlags.Module) { const name = (specifier.propertyName ?? specifier.name).escapedText; const exportSymbol = getExportsOfSymbol(symbol).get(name); - checkImportOrExportSymbolForTypeOnlyMarker(specifier, exportSymbol); + markSymbolOfAliasDeclarationIfResolvesToTypeOnly(specifier, exportSymbol); return resolveSymbol(exportSymbol, dontResolveAlias); } } @@ -2416,7 +2416,7 @@ namespace ts { } function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol | undefined { - checkAliasDeclarationForTypeOnlyMarker(node); + markSymbolOfAliasDeclarationIfTypeOnly(node); return getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias); } @@ -2425,7 +2425,7 @@ namespace ts { } function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) { - checkAliasDeclarationForTypeOnlyMarker(node); + markSymbolOfAliasDeclarationIfTypeOnly(node); return node.parent.parent.moduleSpecifier ? getExternalModuleMember(node.parent.parent, node, dontResolveAlias) : resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias); @@ -2530,21 +2530,21 @@ namespace ts { return links.target; } - function checkImportOrExportSymbolForTypeOnlyMarker(sourceNode: TypeOnlyCompatibleAliasDeclaration | undefined, symbol: Symbol | undefined) { - if (!sourceNode || !symbol) return; - const sourceSymbol = getSymbolOfNode(sourceNode); + function markSymbolOfAliasDeclarationIfResolvesToTypeOnly(aliasDeclaration: TypeOnlyCompatibleAliasDeclaration | undefined, resolvesToSymbol: Symbol | undefined) { + if (!aliasDeclaration || !resolvesToSymbol) return; + const sourceSymbol = getSymbolOfNode(aliasDeclaration); const links = getSymbolLinks(sourceSymbol); if (links.typeOnlyDeclaration === undefined) { - const typeOnly = find(symbol.declarations, isTypeOnlyImportOrExportDeclaration); - links.typeOnlyDeclaration = typeOnly ?? getSymbolLinks(symbol).typeOnlyDeclaration ?? false; + const typeOnly = find(resolvesToSymbol.declarations, isTypeOnlyImportOrExportDeclaration); + links.typeOnlyDeclaration = typeOnly ?? getSymbolLinks(resolvesToSymbol).typeOnlyDeclaration ?? false; } } - function checkAliasDeclarationForTypeOnlyMarker(node: TypeOnlyCompatibleAliasDeclaration) { - if (isTypeOnlyImportOrExportDeclaration(node)) { - const symbol = getSymbolOfNode(node); + function markSymbolOfAliasDeclarationIfTypeOnly(aliasDeclaration: TypeOnlyCompatibleAliasDeclaration) { + if (isTypeOnlyImportOrExportDeclaration(aliasDeclaration)) { + const symbol = getSymbolOfNode(aliasDeclaration); const links = getSymbolLinks(symbol); - links.typeOnlyDeclaration = node; + links.typeOnlyDeclaration = aliasDeclaration; } } @@ -2684,7 +2684,7 @@ namespace ts { } Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); if (isIdentifier(name) && symbol.flags & SymbolFlags.Alias) { - checkImportOrExportSymbolForTypeOnlyMarker(getTypeOnlyCompatibleAliasDeclarationFromName(name), symbol); + markSymbolOfAliasDeclarationIfResolvesToTypeOnly(getTypeOnlyCompatibleAliasDeclarationFromName(name), symbol); } return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol); }