diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 81b188e1a8ed5..869e4a032936e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -517,8 +517,13 @@ namespace ts { } } - const declarationName = getNameOfDeclaration(node) || node; const relatedInformation: DiagnosticRelatedInformation[] = []; + if (isTypeAliasDeclaration(node) && nodeIsMissing(node.type) && hasModifier(node, ModifierFlags.Export) && symbol.flags & (SymbolFlags.Alias | SymbolFlags.Type | SymbolFlags.Namespace)) { + // export type T; - may have meant export type { T }? + relatedInformation.push(createDiagnosticForNode(node, Diagnostics.Did_you_mean_0, `export type { ${unescapeLeadingUnderscores(node.name.escapedText)} }`)); + } + + const declarationName = getNameOfDeclaration(node) || node; forEach(symbol.declarations, (declaration, index) => { const decl = getNameOfDeclaration(declaration) || declaration; const diag = createDiagnosticForNode(decl, message, messageNeedsName ? getDisplayName(declaration) : undefined); @@ -531,7 +536,7 @@ namespace ts { }); const diag = createDiagnosticForNode(declarationName, message, messageNeedsName ? getDisplayName(node) : undefined); - file.bindDiagnostics.push(multipleDefaultExports ? addRelatedInfo(diag, ...relatedInformation) : diag); + file.bindDiagnostics.push(addRelatedInfo(diag, ...relatedInformation)); symbol = createSymbol(SymbolFlags.None, name); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7df196699f8fe..27c4def26f914 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -989,10 +989,6 @@ namespace ts { return symbol; } - function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol { - return (symbol.flags & SymbolFlags.Transient) !== 0; - } - function getExcludedSymbolFlags(flags: SymbolFlags): SymbolFlags { let result: SymbolFlags = 0; if (flags & SymbolFlags.BlockScopedVariable) result |= SymbolFlags.BlockScopedVariableExcludes; @@ -2047,7 +2043,12 @@ namespace ts { if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) { const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); if (symbol) { - error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, unescapeLeadingUnderscores(name)); + 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, + unescapeLeadingUnderscores(name)); return true; } } @@ -2221,15 +2222,25 @@ namespace ts { } else if (hasSyntheticDefault) { // per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present - return resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); + return maybeTypeOnly( + 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 exportDefaultSymbol; + return symbol; } } function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol | undefined { const moduleSpecifier = node.parent.parent.moduleSpecifier; - return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false); + const moduleSymbol = resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false); + return moduleSymbol && node.parent.isTypeOnly ? createTypeOnlySymbol(moduleSymbol) : moduleSymbol; } function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined { @@ -2348,17 +2359,98 @@ namespace ts { } function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol | undefined { - return getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias); + const resolved = getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias); + if (resolved && node.parent.parent.isTypeOnly) { + return createTypeOnlyImportOrExport(node.name, resolved); + } + return resolved; } 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) { - return node.parent.parent.moduleSpecifier ? + const target = 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 { @@ -8155,13 +8247,20 @@ namespace ts { return errorType; } - const declaration = find(symbol.declarations, isTypeAlias); - if (!declaration) { - return Debug.fail("Type alias symbol with no valid declaration found"); + 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 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); @@ -8175,7 +8274,7 @@ namespace ts { } else { type = errorType; - error(isJSDocEnumTag(declaration) ? declaration : declaration.name || declaration, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); + error(isNamedDeclaration(declaration) ? declaration.name : declaration || declaration, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); } links.declaredType = type; } @@ -10890,6 +10989,9 @@ 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); } @@ -32941,9 +33043,10 @@ namespace ts { // Don't allow to re-export something with no value side when `--isolatedModules` is set. if (compilerOptions.isolatedModules && node.kind === SyntaxKind.ExportSpecifier + && !node.parent.parent.isTypeOnly && !(target.flags & SymbolFlags.Value) && !(node.flags & NodeFlags.Ambient)) { - error(node, Diagnostics.Cannot_re_export_a_type_when_the_isolatedModules_flag_is_provided); + error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type); } } } @@ -32964,7 +33067,7 @@ namespace ts { } if (checkExternalImportOrExportDeclaration(node)) { const importClause = node.importClause; - if (importClause) { + if (importClause && !checkGrammarImportClause(importClause)) { if (importClause.name) { checkImportBinding(importClause); } @@ -33069,6 +33172,40 @@ namespace ts { return !isInAppropriateContext; } + function importClauseContainsReferencedImport(importClause: ImportClause) { + return importClause.name && isReferenced(importClause) + || importClause.namedBindings && namedBindingsContainsReferencedImport(importClause.namedBindings); + + function isReferenced(declaration: Declaration) { + return !!getMergedSymbol(getSymbolOfNode(declaration)).isReferenced; + } + function namedBindingsContainsReferencedImport(namedBindings: NamedImportBindings) { + return isNamespaceImport(namedBindings) + ? isReferenced(namedBindings) + : some(namedBindings.elements, isReferenced); + } + } + + function checkImportsForTypeOnlyConversion(sourceFile: SourceFile) { + for (const statement of sourceFile.statements) { + if ( + isImportDeclaration(statement) && + statement.importClause && + !statement.importClause.isTypeOnly && + importClauseContainsReferencedImport(statement.importClause) && + !isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) + ) { + const isError = compilerOptions.importsNotUsedAsValue === ImportsNotUsedAsValue.Error; + errorOrSuggestion( + isError, + statement, + isError + ? Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValue_is_set_to_error + : Diagnostics.This_import_may_be_converted_to_a_type_only_import); + } + } + } + function checkExportSpecifier(node: ExportSpecifier) { checkAliasSymbol(node); if (getEmitDeclarations(compilerOptions)) { @@ -33561,6 +33698,10 @@ namespace ts { }); } + if (!node.isDeclarationFile && isExternalModule(node)) { + checkImportsForTypeOnlyConversion(node); + } + if (isExternalOrCommonJsModule(node)) { checkExternalModuleExports(node); } @@ -33743,10 +33884,10 @@ namespace ts { function isTypeDeclarationName(name: Node): boolean { return name.kind === SyntaxKind.Identifier && isTypeDeclaration(name.parent) && - (name.parent).name === name; + name.parent.name === name; } - function isTypeDeclaration(node: Node): node is TypeParameterDeclaration | ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumDeclaration { + function isTypeDeclaration(node: Node): node is TypeParameterDeclaration | ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumDeclaration | ImportClause | ImportSpecifier | ExportSpecifier { switch (node.kind) { case SyntaxKind.TypeParameter: case SyntaxKind.ClassDeclaration: @@ -33754,6 +33895,11 @@ namespace ts { case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.EnumDeclaration: return true; + case SyntaxKind.ImportClause: + return (node as ImportClause).isTypeOnly; + case SyntaxKind.ImportSpecifier: + case SyntaxKind.ExportSpecifier: + return (node as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; default: return false; } @@ -34142,7 +34288,10 @@ namespace ts { if (isDeclarationNameOrImportPropertyName(node)) { const symbol = getSymbolAtLocation(node); - return symbol ? getTypeOfSymbol(symbol) : errorType; + if (symbol) { + return isTypeOnlyImportOrExportName(node) ? getDeclaredTypeOfSymbol(symbol) : getTypeOfSymbol(symbol); + } + return errorType; } if (isBindingPattern(node)) { @@ -36642,6 +36791,13 @@ namespace ts { return ambientModulesCache; } + function checkGrammarImportClause(node: ImportClause): boolean { + if (node.isTypeOnly && node.name && node.namedBindings) { + return grammarErrorOnNode(node, Diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both); + } + return false; + } + function checkGrammarImportCallExpression(node: ImportCall): boolean { if (moduleKind === ModuleKind.ES2015) { return grammarErrorOnNode(node, Diagnostics.Dynamic_imports_are_only_supported_when_the_module_flag_is_set_to_es2020_esnext_commonjs_amd_system_or_umd); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index b0b4c69ca1e15..76477a6fede8b 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -463,6 +463,17 @@ namespace ts { category: Diagnostics.Basic_Options, description: Diagnostics.Import_emit_helpers_from_tslib }, + { + name: "importsNotUsedAsValue", + type: createMapFromTemplate({ + remove: ImportsNotUsedAsValue.Remove, + preserve: ImportsNotUsedAsValue.Preserve, + error: ImportsNotUsedAsValue.Error + }), + affectsEmit: true, + category: Diagnostics.Advanced_Options, + description: Diagnostics.Specify_emit_Slashchecking_behavior_for_imports_that_are_only_used_for_types + }, { name: "downlevelIteration", type: "boolean", diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 203835b49f6a9..cd81020c0f1b7 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1509,6 +1509,13 @@ namespace ts { return compareComparableValues(a, b); } + /** + * Compare two TextSpans, first by `start`, then by `length`. + */ + export function compareTextSpans(a: Partial | undefined, b: Partial | undefined): Comparison { + return compareValues(a?.start, b?.start) || compareValues(a?.length, b?.length); + } + export function min(a: T, b: T, compare: Comparer): T { return compare(a, b) === Comparison.LessThan ? a : b; } @@ -1914,10 +1921,10 @@ namespace ts { return (arg: T) => f(arg) && g(arg); } - export function or(...fs: ((arg: T) => boolean)[]): (arg: T) => boolean { - return arg => { + export function or(...fs: ((...args: T) => boolean)[]): (...args: T) => boolean { + return (...args) => { for (const f of fs) { - if (f(arg)) { + if (f(...args)) { return true; } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f7949496bfee2..43683086cde67 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -639,7 +639,7 @@ "category": "Error", "code": 1203 }, - "Cannot re-export a type when the '--isolatedModules' flag is provided.": { + "Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.": { "category": "Error", "code": 1205 }, @@ -1059,10 +1059,66 @@ "category": "Error", "code": 1360 }, - "'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher.": { + "Type-only {0} must reference a type, but '{1}' is a value.": { "category": "Error", "code": 1361 }, + "Enum '{0}' cannot be used as a value because only its type has been imported.": { + "category": "Error", + "code": 1362 + }, + "A type-only import can specify a default import or named bindings, but not both.": { + "category": "Error", + "code": 1363 + }, + "Convert to type-only export": { + "category": "Message", + "code": 1364 + }, + "Convert all re-exported types to type-only exports": { + "category": "Message", + "code": 1365 + }, + "Split into two separate import declarations": { + "category": "Message", + "code": 1366 + }, + "Split all invalid type-only imports": { + "category": "Message", + "code": 1377 + }, + "Specify emit/checking behavior for imports that are only used for types": { + "category": "Message", + "code": 1368 + }, + "Did you mean '{0}'?": { + "category": "Message", + "code": 1369 + }, + "Only ECMAScript imports may use 'import type'.": { + "category": "Error", + "code": 1370 + }, + "This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValue' is set to 'error'.": { + "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 + }, + "Convert all imports not used as a value to type-only imports": { + "category": "Message", + "code": 1374 + }, + "'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher.": { + "category": "Error", + "code": 1375 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 48e35cb1e2550..d576083087102 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3048,6 +3048,10 @@ namespace ts { } function emitImportClause(node: ImportClause) { + if (node.isTypeOnly) { + emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node); + writeSpace(); + } emit(node.name); if (node.name && node.namedBindings) { emitTokenWithComment(SyntaxKind.CommaToken, node.name.end, writePunctuation, node); @@ -3089,6 +3093,10 @@ namespace ts { function emitExportDeclaration(node: ExportDeclaration) { let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node); writeSpace(); + if (node.isTypeOnly) { + nextPos = emitTokenWithComment(SyntaxKind.TypeKeyword, nextPos, writeKeyword, node); + writeSpace(); + } if (node.exportClause) { emit(node.exportClause); } diff --git a/src/compiler/factoryPublic.ts b/src/compiler/factoryPublic.ts index 0d9adc26d9cbf..19e927f4f6673 100644 --- a/src/compiler/factoryPublic.ts +++ b/src/compiler/factoryPublic.ts @@ -2267,17 +2267,19 @@ namespace ts { : node; } - export function createImportClause(name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause { + export function createImportClause(name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, isTypeOnly = false): ImportClause { const node = createSynthesizedNode(SyntaxKind.ImportClause); node.name = name; node.namedBindings = namedBindings; + node.isTypeOnly = isTypeOnly; return node; } - export function updateImportClause(node: ImportClause, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined) { + export function updateImportClause(node: ImportClause, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, isTypeOnly: boolean) { return node.name !== name || node.namedBindings !== namedBindings - ? updateNode(createImportClause(name, namedBindings), node) + || node.isTypeOnly !== isTypeOnly + ? updateNode(createImportClause(name, namedBindings, isTypeOnly), node) : node; } @@ -2348,10 +2350,11 @@ namespace ts { : node; } - export function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression) { + export function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression, isTypeOnly = false) { const node = createSynthesizedNode(SyntaxKind.ExportDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); + node.isTypeOnly = isTypeOnly; node.exportClause = exportClause; node.moduleSpecifier = moduleSpecifier; return node; @@ -2362,12 +2365,14 @@ namespace ts { decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, - moduleSpecifier: Expression | undefined) { + moduleSpecifier: Expression | undefined, + isTypeOnly: boolean) { return node.decorators !== decorators || node.modifiers !== modifiers + || node.isTypeOnly !== isTypeOnly || node.exportClause !== exportClause || node.moduleSpecifier !== moduleSpecifier - ? updateNode(createExportDeclaration(decorators, modifiers, exportClause, moduleSpecifier), node) + ? updateNode(createExportDeclaration(decorators, modifiers, exportClause, moduleSpecifier, isTypeOnly), node) : node; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 2287c41431616..28fc666a8da40 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1516,7 +1516,10 @@ namespace ts { if (token() === SyntaxKind.DefaultKeyword) { return lookAhead(nextTokenCanFollowDefaultKeyword); } - return token() !== SyntaxKind.AsteriskToken && token() !== SyntaxKind.AsKeyword && token() !== SyntaxKind.OpenBraceToken && canFollowModifier(); + if (token() === SyntaxKind.TypeKeyword) { + return lookAhead(nextTokenCanFollowExportModifier); + } + return canFollowExportModifier(); case SyntaxKind.DefaultKeyword: return nextTokenCanFollowDefaultKeyword(); case SyntaxKind.StaticKeyword: @@ -1529,6 +1532,18 @@ namespace ts { } } + function canFollowExportModifier(): boolean { + return token() !== SyntaxKind.AsteriskToken + && token() !== SyntaxKind.AsKeyword + && token() !== SyntaxKind.OpenBraceToken + && canFollowModifier(); + } + + function nextTokenCanFollowExportModifier(): boolean { + nextToken(); + return canFollowExportModifier(); + } + function parseAnyContextualModifier(): boolean { return isModifierKind(token()) && tryParse(nextTokenCanFollowModifier); } @@ -5470,10 +5485,13 @@ namespace ts { return token() === SyntaxKind.StringLiteral || token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.OpenBraceToken || tokenIsIdentifierOrKeyword(token()); case SyntaxKind.ExportKeyword: - nextToken(); - if (token() === SyntaxKind.EqualsToken || token() === SyntaxKind.AsteriskToken || - token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.DefaultKeyword || - token() === SyntaxKind.AsKeyword) { + let currentToken = nextToken(); + if (currentToken === SyntaxKind.TypeKeyword) { + currentToken = lookAhead(nextToken); + } + if (currentToken === SyntaxKind.EqualsToken || currentToken === SyntaxKind.AsteriskToken || + currentToken === SyntaxKind.OpenBraceToken || currentToken === SyntaxKind.DefaultKeyword || + currentToken === SyntaxKind.AsKeyword) { return true; } continue; @@ -6355,9 +6373,19 @@ namespace ts { let identifier: Identifier | undefined; if (isIdentifier()) { identifier = parseIdentifier(); - if (token() !== SyntaxKind.CommaToken && token() !== SyntaxKind.FromKeyword) { - return parseImportEqualsDeclaration(node, identifier); - } + } + + let isTypeOnly = false; + if (token() !== SyntaxKind.FromKeyword && + identifier?.escapedText === "type" && + (isIdentifier() || tokenAfterImportDefinitelyProducesImportDeclaration()) + ) { + isTypeOnly = true; + identifier = isIdentifier() ? parseIdentifier() : undefined; + } + + if (identifier && !tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration()) { + return parseImportEqualsDeclaration(node, identifier, isTypeOnly); } // Import statement @@ -6366,9 +6394,10 @@ namespace ts { // import ImportClause from ModuleSpecifier ; // import ModuleSpecifier; if (identifier || // import id - token() === SyntaxKind.AsteriskToken || // import * - token() === SyntaxKind.OpenBraceToken) { // import { - (node).importClause = parseImportClause(identifier, afterImportPos); + token() === SyntaxKind.AsteriskToken || // import * + token() === SyntaxKind.OpenBraceToken // import { + ) { + (node).importClause = parseImportClause(identifier, afterImportPos, isTypeOnly); parseExpected(SyntaxKind.FromKeyword); } @@ -6377,16 +6406,30 @@ namespace ts { return finishNode(node); } - function parseImportEqualsDeclaration(node: ImportEqualsDeclaration, identifier: Identifier): ImportEqualsDeclaration { + function tokenAfterImportDefinitelyProducesImportDeclaration() { + return token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.OpenBraceToken; + } + + function tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration() { + // In `import id ___`, the current token decides whether to produce + // an ImportDeclaration or ImportEqualsDeclaration. + return token() === SyntaxKind.CommaToken || token() === SyntaxKind.FromKeyword; + } + + function parseImportEqualsDeclaration(node: ImportEqualsDeclaration, identifier: Identifier, isTypeOnly: boolean): ImportEqualsDeclaration { node.kind = SyntaxKind.ImportEqualsDeclaration; node.name = identifier; parseExpected(SyntaxKind.EqualsToken); node.moduleReference = parseModuleReference(); parseSemicolon(); - return finishNode(node); + const finished = finishNode(node); + if (isTypeOnly) { + parseErrorAtRange(finished, Diagnostics.Only_ECMAScript_imports_may_use_import_type); + } + return finished; } - function parseImportClause(identifier: Identifier | undefined, fullStart: number) { + function parseImportClause(identifier: Identifier | undefined, fullStart: number, isTypeOnly: boolean) { // ImportClause: // ImportedDefaultBinding // NameSpaceImport @@ -6395,6 +6438,8 @@ namespace ts { // ImportedDefaultBinding, NamedImports const importClause = createNode(SyntaxKind.ImportClause, fullStart); + importClause.isTypeOnly = isTypeOnly; + if (identifier) { // ImportedDefaultBinding: // ImportedBinding @@ -6514,6 +6559,7 @@ namespace ts { function parseExportDeclaration(node: ExportDeclaration): ExportDeclaration { node.kind = SyntaxKind.ExportDeclaration; + node.isTypeOnly = parseOptional(SyntaxKind.TypeKeyword); if (parseOptional(SyntaxKind.AsteriskToken)) { if (parseOptional(SyntaxKind.AsKeyword)) { node.exportClause = parseNamespaceExport(); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 891763ddbbd33..3a23230b4c382 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1820,6 +1820,18 @@ namespace ts { } switch (node.kind) { + case SyntaxKind.ImportClause: + if ((node as ImportClause).isTypeOnly) { + diagnostics.push(createDiagnosticForNode(node.parent, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "import type")); + return; + } + break; + case SyntaxKind.ExportDeclaration: + if ((node as ExportDeclaration).isTypeOnly) { + diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "export type")); + return; + } + break; case SyntaxKind.ImportEqualsDeclaration: diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_TypeScript_files)); return; diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 0b81d2bb32043..e2dbe8a2bd34e 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -686,7 +686,8 @@ namespace ts { return visibleDefaultBinding && updateImportDeclaration(decl, /*decorators*/ undefined, decl.modifiers, updateImportClause( decl.importClause, visibleDefaultBinding, - /*namedBindings*/ undefined + /*namedBindings*/ undefined, + decl.importClause.isTypeOnly, ), rewriteModuleSpecifier(decl, decl.moduleSpecifier)); } if (decl.importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { @@ -695,7 +696,8 @@ namespace ts { return visibleDefaultBinding || namedBindings ? updateImportDeclaration(decl, /*decorators*/ undefined, decl.modifiers, updateImportClause( decl.importClause, visibleDefaultBinding, - namedBindings + namedBindings, + decl.importClause.isTypeOnly, ), rewriteModuleSpecifier(decl, decl.moduleSpecifier)) : undefined; } // Named imports (optionally with visible default) @@ -708,7 +710,8 @@ namespace ts { updateImportClause( decl.importClause, visibleDefaultBinding, - bindingList && bindingList.length ? updateNamedImports(decl.importClause.namedBindings, bindingList) : undefined + bindingList && bindingList.length ? updateNamedImports(decl.importClause.namedBindings, bindingList) : undefined, + decl.importClause.isTypeOnly, ), rewriteModuleSpecifier(decl, decl.moduleSpecifier) ); @@ -1033,7 +1036,13 @@ namespace ts { resultHasScopeMarker = true; // Always visible if the parent node isn't dropped for being not visible // Rewrite external module names if necessary - return updateExportDeclaration(input, /*decorators*/ undefined, input.modifiers, input.exportClause, rewriteModuleSpecifier(input, input.moduleSpecifier)); + return updateExportDeclaration( + input, + /*decorators*/ undefined, + input.modifiers, + input.exportClause, + rewriteModuleSpecifier(input, input.moduleSpecifier), + input.isTypeOnly); } case SyntaxKind.ExportAssignment: { // Always visible if the parent node isn't dropped for being not visible diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 3b83356acbfb7..e855981114f37 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2760,7 +2760,7 @@ namespace ts { } /** - * Visits an import declaration, eliding it if it is not referenced. + * Visits an import declaration, eliding it if it is not referenced and `importsNotUsedAsValue` is not 'preserve'. * * @param node The import declaration node. */ @@ -2770,10 +2770,16 @@ namespace ts { // import "foo"; return node; } + if (node.importClause.isTypeOnly) { + // Always elide type-only imports + return undefined; + } // Elide the declaration if the import clause was elided. const importClause = visitNode(node.importClause, visitImportClause, isImportClause); - return importClause + return importClause || + compilerOptions.importsNotUsedAsValue === ImportsNotUsedAsValue.Preserve || + compilerOptions.importsNotUsedAsValue === ImportsNotUsedAsValue.Error ? updateImportDeclaration( node, /*decorators*/ undefined, @@ -2789,10 +2795,13 @@ namespace ts { * @param node The import clause node. */ function visitImportClause(node: ImportClause): VisitResult { + if (node.isTypeOnly) { + return undefined; + } // Elide the import clause if we elide both its name and its named bindings. const name = resolver.isReferencedAliasDeclaration(node) ? node.name : undefined; const namedBindings = visitNode(node.namedBindings, visitNamedImportBindings, isNamedImportBindings); - return (name || namedBindings) ? updateImportClause(node, name, namedBindings) : undefined; + return (name || namedBindings) ? updateImportClause(node, name, namedBindings, /*isTypeOnly*/ false) : undefined; } /** @@ -2842,6 +2851,10 @@ namespace ts { * @param node The export declaration node. */ function visitExportDeclaration(node: ExportDeclaration): VisitResult { + if (node.isTypeOnly) { + return undefined; + } + if (!node.exportClause) { // Elide a star export if the module it references does not export a value. return compilerOptions.isolatedModules || resolver.moduleExportsSomeValue(node.moduleSpecifier!) ? node : undefined; @@ -2860,7 +2873,8 @@ namespace ts { /*decorators*/ undefined, /*modifiers*/ undefined, exportClause, - node.moduleSpecifier) + node.moduleSpecifier, + node.isTypeOnly) : undefined; } @@ -2915,10 +2929,24 @@ namespace ts { */ function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult { if (isExternalModuleImportEqualsDeclaration(node)) { - // Elide external module `import=` if it is not referenced. - return resolver.isReferencedAliasDeclaration(node) - ? visitEachChild(node, visitor, context) - : undefined; + const isReferenced = resolver.isReferencedAliasDeclaration(node); + // If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'. + if (!isReferenced && compilerOptions.importsNotUsedAsValue === ImportsNotUsedAsValue.Preserve) { + return setOriginalNode( + setTextRange( + createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*importClause*/ undefined, + node.moduleReference.expression, + ), + node, + ), + node, + ); + } + + return isReferenced ? visitEachChild(node, visitor, context) : undefined; } if (!shouldEmitImportEqualsDeclaration(node)) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 107f2b8155205..36275107b10f6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2477,6 +2477,7 @@ namespace ts { export interface ImportClause extends NamedDeclaration { kind: SyntaxKind.ImportClause; parent: ImportDeclaration; + isTypeOnly: boolean; name?: Identifier; // Default binding namedBindings?: NamedImportBindings; } @@ -2501,6 +2502,7 @@ namespace ts { export interface ExportDeclaration extends DeclarationStatement, JSDocContainer { kind: SyntaxKind.ExportDeclaration; parent: SourceFile | ModuleBlock; + isTypeOnly: boolean; /** Will not be assigned in the case of `export * from "foo";` */ exportClause?: NamedExportBindings; /** If this is not a StringLiteral it will be a grammar error. */ @@ -5041,6 +5043,7 @@ namespace ts { /*@internal*/generateCpuProfile?: string; /*@internal*/help?: boolean; importHelpers?: boolean; + importsNotUsedAsValue?: ImportsNotUsedAsValue; /*@internal*/init?: boolean; inlineSourceMap?: boolean; inlineSources?: boolean; @@ -5161,6 +5164,12 @@ namespace ts { ReactNative = 3 } + export const enum ImportsNotUsedAsValue { + Remove, + Preserve, + Error + } + export const enum NewLineKind { CarriageReturnLineFeed = 0, LineFeed = 1 diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 93343650841c7..e8eb00e21f23c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -40,6 +40,18 @@ namespace ts { return result; } + export function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol { + 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 { @@ -1767,7 +1779,7 @@ namespace ts { } } - export function isExternalModuleImportEqualsDeclaration(node: Node) { + export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } { return node.kind === SyntaxKind.ImportEqualsDeclaration && (node).moduleReference.kind === SyntaxKind.ExternalModuleReference; } @@ -5879,6 +5891,9 @@ namespace ts { } export function addRelatedInfo(diagnostic: T, ...relatedInformation: DiagnosticRelatedInformation[]): T { + if (!relatedInformation.length) { + return diagnostic; + } if (!diagnostic.relatedInformation) { diagnostic.relatedInformation = []; } diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 1dc3a87742ebe..d4bc403b85908 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1721,6 +1721,21 @@ namespace ts { return isImportSpecifier(node) || isExportSpecifier(node); } + export function isTypeOnlyImportOrExportName(node: Node): boolean { + if (node.kind !== SyntaxKind.Identifier) { + return false; + } + switch (node.parent.kind) { + case SyntaxKind.ImportSpecifier: + case SyntaxKind.ExportSpecifier: + return (node.parent as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly; + case SyntaxKind.ImportClause: + return (node.parent as ImportClause).isTypeOnly; + default: + return false; + } + } + export function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken { return node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind); } diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 0a40cd473305e..3cbd60ff51614 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -792,7 +792,8 @@ namespace ts { case SyntaxKind.ImportClause: return updateImportClause(node, visitNode((node).name, visitor, isIdentifier), - visitNode((node).namedBindings, visitor, isNamedImportBindings)); + visitNode((node).namedBindings, visitor, isNamedImportBindings), + (node as ImportClause).isTypeOnly); case SyntaxKind.NamespaceImport: return updateNamespaceImport(node, @@ -822,7 +823,8 @@ namespace ts { nodesVisitor((node).decorators, visitor, isDecorator), nodesVisitor((node).modifiers, visitor, isModifier), visitNode((node).exportClause, visitor, isNamedExportBindings), - visitNode((node).moduleSpecifier, visitor, isExpression)); + visitNode((node).moduleSpecifier, visitor, isExpression), + (node as ExportDeclaration).isTypeOnly); case SyntaxKind.NamedExports: return updateNamedExports(node, diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 952ba36b7598e..972c191f189f0 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -738,7 +738,15 @@ namespace FourSlash { } if (!range) { - this.raiseError(`goToDefinitionsAndBoundSpan failed - found a TextSpan ${JSON.stringify(defs.textSpan)} when it wasn't expected.`); + const marker = this.getMarkerByName(startMarkerName); + const startFile = marker.fileName; + const fileContent = this.getFileContent(startFile); + const spanContent = fileContent.slice(defs.textSpan.start, ts.textSpanEnd(defs.textSpan)); + const spanContentWithMarker = spanContent.slice(0, marker.position - defs.textSpan.start) + `/*${startMarkerName}*/` + spanContent.slice(marker.position - defs.textSpan.start); + const suggestedFileContent = (fileContent.slice(0, defs.textSpan.start) + `\x1b[1;4m[|${spanContentWithMarker}|]\x1b[31m` + fileContent.slice(ts.textSpanEnd(defs.textSpan))) + .split(/\r?\n/).map(line => " ".repeat(6) + line).join(ts.sys.newLine); + this.raiseError(`goToDefinitionsAndBoundSpan failed. Found a starting TextSpan around '${spanContent}' in '${startFile}' (at position ${defs.textSpan.start}). ` + + `If this is the correct input span, put a fourslash range around it: \n\n${suggestedFileContent}\n`); } else { this.assertTextSpanEqualsRange(defs.textSpan, range, "goToDefinitionsAndBoundSpan failed"); diff --git a/src/services/codefixes/convertToTypeOnlyExport.ts b/src/services/codefixes/convertToTypeOnlyExport.ts new file mode 100644 index 0000000000000..7adf4d1aacd67 --- /dev/null +++ b/src/services/codefixes/convertToTypeOnlyExport.ts @@ -0,0 +1,83 @@ +/* @internal */ +namespace ts.codefix { + const errorCodes = [Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type.code]; + const fixId = "convertToTypeOnlyExport"; + registerCodeFix({ + errorCodes, + getCodeActions: context => { + const changes = textChanges.ChangeTracker.with(context, t => fixSingleExportDeclaration(t, getExportSpecifierForDiagnosticSpan(context.span, context.sourceFile), context)); + if (changes.length) { + return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_type_only_export, fixId, Diagnostics.Convert_all_re_exported_types_to_type_only_exports)]; + } + }, + fixIds: [fixId], + getAllCodeActions: context => { + const fixedExportDeclarations = createMap(); + return codeFixAll(context, errorCodes, (changes, diag) => { + const exportSpecifier = getExportSpecifierForDiagnosticSpan(diag, context.sourceFile); + if (exportSpecifier && !addToSeen(fixedExportDeclarations, getNodeId(exportSpecifier.parent.parent))) { + fixSingleExportDeclaration(changes, exportSpecifier, context); + } + }); + } + }); + + function getExportSpecifierForDiagnosticSpan(span: TextSpan, sourceFile: SourceFile) { + return tryCast(getTokenAtPosition(sourceFile, span.start).parent, isExportSpecifier); + } + + function fixSingleExportDeclaration(changes: textChanges.ChangeTracker, exportSpecifier: ExportSpecifier | undefined, context: CodeFixContextBase) { + if (!exportSpecifier) { + return; + } + + const exportClause = exportSpecifier.parent; + const exportDeclaration = exportClause.parent; + const typeExportSpecifiers = getTypeExportSpecifiers(exportSpecifier, context); + if (typeExportSpecifiers.length === exportClause.elements.length) { + changes.replaceNode( + context.sourceFile, + exportDeclaration, + updateExportDeclaration( + exportDeclaration, + exportDeclaration.decorators, + exportDeclaration.modifiers, + exportClause, + exportDeclaration.moduleSpecifier, + /*isTypeOnly*/ true)); + } + else { + const valueExportDeclaration = updateExportDeclaration( + exportDeclaration, + exportDeclaration.decorators, + exportDeclaration.modifiers, + updateNamedExports(exportClause, filter(exportClause.elements, e => !contains(typeExportSpecifiers, e))), + exportDeclaration.moduleSpecifier, + /*isTypeOnly*/ false); + const typeExportDeclaration = createExportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createNamedExports(typeExportSpecifiers), + exportDeclaration.moduleSpecifier, + /*isTypeOnly*/ true); + + changes.replaceNode(context.sourceFile, exportDeclaration, valueExportDeclaration); + changes.insertNodeAfter(context.sourceFile, exportDeclaration, typeExportDeclaration); + } + } + + function getTypeExportSpecifiers(originExportSpecifier: ExportSpecifier, context: CodeFixContextBase): readonly ExportSpecifier[] { + const exportClause = originExportSpecifier.parent; + if (exportClause.elements.length === 1) { + return exportClause.elements; + } + + const diagnostics = getDiagnosticsWithinSpan( + createTextSpanFromNode(exportClause), + context.program.getSemanticDiagnostics(context.sourceFile, context.cancellationToken)); + + return filter(exportClause.elements, element => { + return element === originExportSpecifier || findDiagnosticForNode(element, diagnostics)?.code === errorCodes[0]; + }); + } +} diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 1581a8b64feb4..e5e141407cb6c 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -30,8 +30,7 @@ namespace ts.codefix { const importType: FixUseImportType[] = []; // Keys are import clause node IDs. const addToExisting = createMap<{ readonly importClause: ImportClause, defaultImport: string | undefined; readonly namedImports: string[] }>(); - // Keys are module specifiers. - const newImports = createMap>(); + const newImports = createNewImportMap(); eachDiagnostic(context, errorCodes, diag => { const info = getFixesInfo(context, diag.code, diag.start); @@ -63,10 +62,10 @@ namespace ts.codefix { break; } case ImportFixKind.AddNew: { - const { moduleSpecifier, importKind } = fix; - let entry = newImports.get(moduleSpecifier); + const { moduleSpecifier, importKind, typeOnly } = fix; + let entry = newImports.get(moduleSpecifier, typeOnly); if (!entry) { - newImports.set(moduleSpecifier, entry = { defaultImport: undefined, namedImports: [], namespaceLikeImport: undefined }); + newImports.set(moduleSpecifier, typeOnly, entry = { defaultImport: undefined, namedImports: [], namespaceLikeImport: undefined }); } switch (importKind) { case ImportKind.Default: @@ -100,13 +99,38 @@ namespace ts.codefix { addToExisting.forEach(({ importClause, defaultImport, namedImports }) => { doAddExistingFix(changes, sourceFile, importClause, defaultImport, namedImports); }); - newImports.forEach((imports, moduleSpecifier) => { - addNewImports(changes, sourceFile, moduleSpecifier, quotePreference, imports); + newImports.forEach((imports, moduleSpecifier, typeOnly) => { + addNewImports(changes, sourceFile, typeOnly, moduleSpecifier, quotePreference, imports); }); })); }, }); + function createNewImportMap() { + // Keys are module specifiers. + const newImports = createMap>(); + return { + get: (moduleSpecifier: string, typeOnly: boolean) => { + return newImports.get(key(moduleSpecifier, typeOnly)); + }, + set: (moduleSpecifier: string, typeOnly: boolean, value: ImportsCollection) => { + return newImports.set(key(moduleSpecifier, typeOnly), value); + }, + forEach: (action: (value: ImportsCollection, moduleSpecifier: string, typeOnly: boolean) => void) => { + newImports.forEach((value, key) => { + const typeOnly = !!+key[0]; + const moduleSpecifier = key.slice(1); + action(value, moduleSpecifier, typeOnly); + }); + }, + }; + + function key(moduleSpecifier: string, typeOnly: boolean) { + const prefix = typeOnly ? "1" : "0"; + return prefix + moduleSpecifier; + } + } + // Sorted with the preferred fix coming first. const enum ImportFixKind { UseNamespace, ImportType, AddToExisting, AddNew } type ImportFix = FixUseNamespaceImport | FixUseImportType | FixAddToExistingImport | FixAddNewImport; @@ -129,6 +153,7 @@ namespace ts.codefix { readonly kind: ImportFixKind.AddNew; readonly moduleSpecifier: string; readonly importKind: ImportKind; + readonly typeOnly: boolean; } const enum ImportKind { @@ -151,6 +176,7 @@ namespace ts.codefix { interface FixAddToExistingImportInfo { readonly declaration: AnyImportSyntax; readonly importKind: ImportKind; + readonly exportedSymbolIsTypeOnly: boolean; } export function getImportCompletionAction( @@ -271,7 +297,7 @@ namespace ts.codefix { return exportedSymbolIsTypeOnly && isSourceFileJS(sourceFile) ? emptyArray : mapDefined(sourceFile.imports, moduleSpecifier => { const i = importFromModuleSpecifier(moduleSpecifier); return (i.kind === SyntaxKind.ImportDeclaration || i.kind === SyntaxKind.ImportEqualsDeclaration) - && checker.getSymbolAtLocation(moduleSpecifier) === moduleSymbol ? { declaration: i, importKind } : undefined; + && checker.getSymbolAtLocation(moduleSpecifier) === moduleSymbol ? { declaration: i, importKind, exportedSymbolIsTypeOnly } : undefined; }); } @@ -291,7 +317,7 @@ namespace ts.codefix { // `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types. exportedSymbolIsTypeOnly && isJs ? { kind: ImportFixKind.ImportType, moduleSpecifier, position: Debug.assertDefined(position, "position should be defined") } - : { kind: ImportFixKind.AddNew, moduleSpecifier, importKind })); + : { kind: ImportFixKind.AddNew, moduleSpecifier, importKind, typeOnly: exportedSymbolIsTypeOnly })); // Sort by presence in package.json, then shortest paths first return sort(choicesForEachExportingModule, (a, b) => { @@ -320,13 +346,15 @@ namespace ts.codefix { return existingDeclaration ? [existingDeclaration] : getNewImportInfos(program, sourceFile, position, exportInfos, host, preferences); } - function newImportInfoFromExistingSpecifier({ declaration, importKind }: FixAddToExistingImportInfo): FixAddNewImport | undefined { + function newImportInfoFromExistingSpecifier({ declaration, importKind, exportedSymbolIsTypeOnly }: FixAddToExistingImportInfo): FixAddNewImport | undefined { const expression = declaration.kind === SyntaxKind.ImportDeclaration ? declaration.moduleSpecifier : declaration.moduleReference.kind === SyntaxKind.ExternalModuleReference ? declaration.moduleReference.expression : undefined; - return expression && isStringLiteral(expression) ? { kind: ImportFixKind.AddNew, moduleSpecifier: expression.text, importKind } : undefined; + return expression && isStringLiteral(expression) + ? { kind: ImportFixKind.AddNew, moduleSpecifier: expression.text, importKind, typeOnly: exportedSymbolIsTypeOnly } + : undefined; } interface FixesInfo { readonly fixes: readonly ImportFix[]; readonly symbolName: string; } @@ -532,7 +560,7 @@ namespace ts.codefix { } case ImportFixKind.AddNew: { const { importKind, moduleSpecifier } = fix; - addNewImports(changes, sourceFile, moduleSpecifier, quotePreference, importKind === ImportKind.Default ? { defaultImport: symbolName, namedImports: emptyArray, namespaceLikeImport: undefined } + addNewImports(changes, sourceFile, fix.typeOnly, moduleSpecifier, quotePreference, importKind === ImportKind.Default ? { defaultImport: symbolName, namedImports: emptyArray, namespaceLikeImport: undefined } : importKind === ImportKind.Named ? { defaultImport: undefined, namedImports: [symbolName], namespaceLikeImport: undefined } : { defaultImport: undefined, namedImports: emptyArray, namespaceLikeImport: { importKind, name: symbolName } }); return [importKind === ImportKind.Default ? Diagnostics.Import_default_0_from_module_1 : Diagnostics.Import_0_from_module_1, symbolName, moduleSpecifier]; @@ -590,13 +618,13 @@ namespace ts.codefix { readonly name: string; } | undefined; } - function addNewImports(changes: textChanges.ChangeTracker, sourceFile: SourceFile, moduleSpecifier: string, quotePreference: QuotePreference, { defaultImport, namedImports, namespaceLikeImport }: ImportsCollection): void { + function addNewImports(changes: textChanges.ChangeTracker, sourceFile: SourceFile, typeOnly: boolean, moduleSpecifier: string, quotePreference: QuotePreference, { defaultImport, namedImports, namespaceLikeImport }: ImportsCollection): void { const quotedModuleSpecifier = makeStringLiteral(moduleSpecifier, quotePreference); if (defaultImport !== undefined || namedImports.length) { insertImport(changes, sourceFile, makeImport( defaultImport === undefined ? undefined : createIdentifier(defaultImport), - namedImports.map(n => createImportSpecifier(/*propertyName*/ undefined, createIdentifier(n))), moduleSpecifier, quotePreference)); + namedImports.map(n => createImportSpecifier(/*propertyName*/ undefined, createIdentifier(n))), moduleSpecifier, quotePreference, typeOnly)); } if (namespaceLikeImport) { insertImport( @@ -604,7 +632,7 @@ namespace ts.codefix { sourceFile, namespaceLikeImport.importKind === ImportKind.Equals ? createImportEqualsDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createIdentifier(namespaceLikeImport.name), createExternalModuleReference(quotedModuleSpecifier)) : namespaceLikeImport.importKind === ImportKind.ConstEquals ? createConstEqualsRequireDeclaration(namespaceLikeImport.name, quotedModuleSpecifier) : - createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(namespaceLikeImport.name))), quotedModuleSpecifier)); + createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(namespaceLikeImport.name)), typeOnly), quotedModuleSpecifier)); } } diff --git a/src/services/codefixes/splitTypeOnlyImport.ts b/src/services/codefixes/splitTypeOnlyImport.ts new file mode 100644 index 0000000000000..e5691a76ab35d --- /dev/null +++ b/src/services/codefixes/splitTypeOnlyImport.ts @@ -0,0 +1,43 @@ +/* @internal */ +namespace ts.codefix { + const errorCodes = [Diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both.code]; + const fixId = "splitTypeOnlyImport"; + registerCodeFix({ + errorCodes, + fixIds: [fixId], + getCodeActions: context => { + const changes = textChanges.ChangeTracker.with(context, t => { + return splitTypeOnlyImport(t, getImportDeclaration(context.sourceFile, context.span), context); + }); + if (changes.length) { + return [createCodeFixAction(fixId, changes, Diagnostics.Split_into_two_separate_import_declarations, fixId, Diagnostics.Split_all_invalid_type_only_imports)]; + } + }, + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, error) => { + splitTypeOnlyImport(changes, getImportDeclaration(context.sourceFile, error), context); + }), + }); + + function getImportDeclaration(sourceFile: SourceFile, span: TextSpan) { + return findAncestor(getTokenAtPosition(sourceFile, span.start), isImportDeclaration); + } + + function splitTypeOnlyImport(changes: textChanges.ChangeTracker, importDeclaration: ImportDeclaration | undefined, context: CodeFixContextBase) { + if (!importDeclaration) { + return; + } + const importClause = Debug.assertDefined(importDeclaration.importClause); + changes.replaceNode(context.sourceFile, importDeclaration, updateImportDeclaration( + importDeclaration, + importDeclaration.decorators, + importDeclaration.modifiers, + updateImportClause(importClause, importClause.name, /*namedBindings*/ undefined, importClause.isTypeOnly), + importDeclaration.moduleSpecifier)); + + changes.insertNodeAfter(context.sourceFile, importDeclaration, createImportDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + updateImportClause(importClause, /*name*/ undefined, importClause.namedBindings, importClause.isTypeOnly), + importDeclaration.moduleSpecifier)); + } +} diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index 232f2c1ba9509..e7731a21f72a1 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -322,7 +322,8 @@ namespace ts.OrganizeImports { updateNamedExports(exportDecl.exportClause, sortedExportSpecifiers) : updateNamespaceExport(exportDecl.exportClause, exportDecl.exportClause.name) ), - exportDecl.moduleSpecifier)); + exportDecl.moduleSpecifier, + exportDecl.isTypeOnly)); return coalescedExports; @@ -362,7 +363,7 @@ namespace ts.OrganizeImports { importDeclaration, importDeclaration.decorators, importDeclaration.modifiers, - updateImportClause(importDeclaration.importClause!, name, namedBindings), // TODO: GH#18217 + updateImportClause(importDeclaration.importClause!, name, namedBindings, importDeclaration.importClause!.isTypeOnly), // TODO: GH#18217 importDeclaration.moduleSpecifier); } diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index 337afdc4214a6..4a330bcb1afb9 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -353,7 +353,7 @@ namespace ts.refactor { changes.replaceNode( sourceFile, importDecl.importClause, - updateImportClause(importDecl.importClause, name, /*namedBindings*/ undefined) + updateImportClause(importDecl.importClause, name, /*namedBindings*/ undefined, importDecl.importClause.isTypeOnly) ); } else if (namedBindings.kind === SyntaxKind.NamedImports) { diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index faff3620d62c2..d71afe7c39553 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -2,6 +2,10 @@ 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; @@ -15,8 +19,6 @@ namespace ts.SymbolDisplay { if (flags & SymbolFlags.Enum) return ScriptElementKind.enumElement; if (flags & SymbolFlags.TypeAlias) return ScriptElementKind.typeElement; if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; - if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; - if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; if (flags & SymbolFlags.EnumMember) return ScriptElementKind.enumMemberElement; if (flags & SymbolFlags.Alias) return ScriptElementKind.alias; @@ -124,6 +126,10 @@ namespace ts.SymbolDisplay { 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/src/services/tsconfig.json b/src/services/tsconfig.json index 674b0076848d4..c1b95bf8fca81 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -58,6 +58,7 @@ "codefixes/convertToAsyncFunction.ts", "codefixes/convertToEs6Module.ts", "codefixes/correctQualifiedNameToIndexedAccessType.ts", + "codefixes/convertToTypeOnlyExport.ts", "codefixes/fixClassIncorrectlyImplementsInterface.ts", "codefixes/importFixes.ts", "codefixes/fixSpelling.ts", @@ -86,6 +87,7 @@ "codefixes/fixAddModuleReferTypeMissingTypeof.ts", "codefixes/convertToMappedObjectType.ts", "codefixes/removeUnnecessaryAwait.ts", + "codefixes/splitTypeOnlyImport.ts", "codefixes/convertConstToLet.ts", "refactors/convertExport.ts", "refactors/convertImport.ts", diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 39ce286486b7a..10a14b9ee8ceb 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1357,12 +1357,12 @@ namespace ts { return defaultImport || namedImports && namedImports.length ? makeImport(defaultImport, namedImports, moduleSpecifier, quotePreference) : undefined; } - export function makeImport(defaultImport: Identifier | undefined, namedImports: readonly ImportSpecifier[] | undefined, moduleSpecifier: string | Expression, quotePreference: QuotePreference): ImportDeclaration { + export function makeImport(defaultImport: Identifier | undefined, namedImports: readonly ImportSpecifier[] | undefined, moduleSpecifier: string | Expression, quotePreference: QuotePreference, isTypeOnly?: boolean): ImportDeclaration { return createImportDeclaration( /*decorators*/ undefined, /*modifiers*/ undefined, defaultImport || namedImports - ? createImportClause(defaultImport, namedImports && namedImports.length ? createNamedImports(namedImports) : undefined) + ? createImportClause(defaultImport, namedImports && namedImports.length ? createNamedImports(namedImports) : undefined, isTypeOnly) : undefined, typeof moduleSpecifier === "string" ? makeStringLiteral(moduleSpecifier, quotePreference) : moduleSpecifier); } @@ -2326,7 +2326,45 @@ namespace ts { export function isInsideNodeModules(fileOrDirectory: string): boolean { return contains(getPathComponents(fileOrDirectory), "node_modules"); } - // #endregion + + export function isDiagnosticWithLocation(diagnostic: Diagnostic): diagnostic is DiagnosticWithLocation { + return diagnostic.file !== undefined && diagnostic.start !== undefined && diagnostic.length !== undefined; + } + + export function findDiagnosticForNode(node: Node, sortedFileDiagnostics: readonly Diagnostic[]): DiagnosticWithLocation | undefined { + const span: Partial = createTextSpanFromNode(node); + const index = binarySearchKey(sortedFileDiagnostics, span, identity, compareTextSpans); + if (index >= 0) { + const diagnostic = sortedFileDiagnostics[index]; + Debug.assertEqual(diagnostic.file, node.getSourceFile(), "Diagnostics proided to 'findDiagnosticForNode' must be from a single SourceFile"); + return cast(diagnostic, isDiagnosticWithLocation); + } + } + + export function getDiagnosticsWithinSpan(span: TextSpan, sortedFileDiagnostics: readonly Diagnostic[]): readonly DiagnosticWithLocation[] { + let index = binarySearchKey(sortedFileDiagnostics, span.start, diag => diag.start, compareValues); + if (index < 0) { + index = ~index; + } + while (sortedFileDiagnostics[index - 1]?.start === span.start) { + index--; + } + + const result: DiagnosticWithLocation[] = []; + const end = textSpanEnd(span); + while (true) { + const diagnostic = tryCast(sortedFileDiagnostics[index], isDiagnosticWithLocation); + if (!diagnostic || diagnostic.start > end) { + break; + } + if (textSpanContainsTextSpan(span, diagnostic)) { + result.push(diagnostic); + } + index++; + } + + return result; + } /* @internal */ export function getRefactorContextSpan({ startPosition, endPosition }: RefactorContext): TextSpan { @@ -2351,4 +2389,6 @@ namespace ts { export function firstOrOnly(valueOrArray: T | readonly T[]): T { return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray; } + + // #endregion } diff --git a/src/testRunner/unittests/transform.ts b/src/testRunner/unittests/transform.ts index fd5b54d3565f5..b244b942a503c 100644 --- a/src/testRunner/unittests/transform.ts +++ b/src/testRunner/unittests/transform.ts @@ -231,7 +231,7 @@ namespace ts { const exports = [{ name: "x" }]; const exportSpecifiers = exports.map(e => createExportSpecifier(e.name, e.name)); const exportClause = createNamedExports(exportSpecifiers); - const newEd = updateExportDeclaration(ed, ed.decorators, ed.modifiers, exportClause, ed.moduleSpecifier); + const newEd = updateExportDeclaration(ed, ed.decorators, ed.modifiers, exportClause, ed.moduleSpecifier, ed.isTypeOnly); return newEd as Node as T; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 0ac0f4c227ee4..5b8624ae0310d 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1496,6 +1496,7 @@ declare namespace ts { export interface ImportClause extends NamedDeclaration { kind: SyntaxKind.ImportClause; parent: ImportDeclaration; + isTypeOnly: boolean; name?: Identifier; namedBindings?: NamedImportBindings; } @@ -1516,6 +1517,7 @@ declare namespace ts { export interface ExportDeclaration extends DeclarationStatement, JSDocContainer { kind: SyntaxKind.ExportDeclaration; parent: SourceFile | ModuleBlock; + isTypeOnly: boolean; /** Will not be assigned in the case of `export * from "foo";` */ exportClause?: NamedExportBindings; /** If this is not a StringLiteral it will be a grammar error. */ @@ -2649,6 +2651,7 @@ declare namespace ts { experimentalDecorators?: boolean; forceConsistentCasingInFileNames?: boolean; importHelpers?: boolean; + importsNotUsedAsValue?: ImportsNotUsedAsValue; inlineSourceMap?: boolean; inlineSources?: boolean; isolatedModules?: boolean; @@ -2746,6 +2749,11 @@ declare namespace ts { React = 2, ReactNative = 3 } + export enum ImportsNotUsedAsValue { + Remove = 0, + Preserve = 1, + Error = 2 + } export enum NewLineKind { CarriageReturnLineFeed = 0, LineFeed = 1 @@ -3730,6 +3738,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 isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken; function isModifier(node: Node): node is Modifier; function isEntityName(node: Node): node is EntityName; @@ -4176,8 +4185,8 @@ declare namespace ts { function updateImportEqualsDeclaration(node: ImportEqualsDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration; function createImportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression): ImportDeclaration; function updateImportDeclaration(node: ImportDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression): ImportDeclaration; - function createImportClause(name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause; - function updateImportClause(node: ImportClause, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause; + function createImportClause(name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, isTypeOnly?: boolean): ImportClause; + function updateImportClause(node: ImportClause, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, isTypeOnly: boolean): ImportClause; function createNamespaceImport(name: Identifier): NamespaceImport; function createNamespaceExport(name: Identifier): NamespaceExport; function updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport; @@ -4188,8 +4197,8 @@ declare namespace ts { function updateImportSpecifier(node: ImportSpecifier, propertyName: Identifier | undefined, name: Identifier): ImportSpecifier; function createExportAssignment(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, isExportEquals: boolean | undefined, expression: Expression): ExportAssignment; function updateExportAssignment(node: ExportAssignment, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, expression: Expression): ExportAssignment; - function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression): ExportDeclaration; - function updateExportDeclaration(node: ExportDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier: Expression | undefined): ExportDeclaration; + function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression, isTypeOnly?: boolean): ExportDeclaration; + function updateExportDeclaration(node: ExportDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier: Expression | undefined, isTypeOnly: boolean): ExportDeclaration; function createNamedExports(elements: readonly ExportSpecifier[]): NamedExports; function updateNamedExports(node: NamedExports, elements: readonly ExportSpecifier[]): NamedExports; function createExportSpecifier(propertyName: string | Identifier | undefined, name: string | Identifier): ExportSpecifier; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 42432eac6e783..65e91049d3319 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1496,6 +1496,7 @@ declare namespace ts { export interface ImportClause extends NamedDeclaration { kind: SyntaxKind.ImportClause; parent: ImportDeclaration; + isTypeOnly: boolean; name?: Identifier; namedBindings?: NamedImportBindings; } @@ -1516,6 +1517,7 @@ declare namespace ts { export interface ExportDeclaration extends DeclarationStatement, JSDocContainer { kind: SyntaxKind.ExportDeclaration; parent: SourceFile | ModuleBlock; + isTypeOnly: boolean; /** Will not be assigned in the case of `export * from "foo";` */ exportClause?: NamedExportBindings; /** If this is not a StringLiteral it will be a grammar error. */ @@ -2649,6 +2651,7 @@ declare namespace ts { experimentalDecorators?: boolean; forceConsistentCasingInFileNames?: boolean; importHelpers?: boolean; + importsNotUsedAsValue?: ImportsNotUsedAsValue; inlineSourceMap?: boolean; inlineSources?: boolean; isolatedModules?: boolean; @@ -2746,6 +2749,11 @@ declare namespace ts { React = 2, ReactNative = 3 } + export enum ImportsNotUsedAsValue { + Remove = 0, + Preserve = 1, + Error = 2 + } export enum NewLineKind { CarriageReturnLineFeed = 0, LineFeed = 1 @@ -3730,6 +3738,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 isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken; function isModifier(node: Node): node is Modifier; function isEntityName(node: Node): node is EntityName; @@ -4176,8 +4185,8 @@ declare namespace ts { function updateImportEqualsDeclaration(node: ImportEqualsDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration; function createImportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression): ImportDeclaration; function updateImportDeclaration(node: ImportDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression): ImportDeclaration; - function createImportClause(name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause; - function updateImportClause(node: ImportClause, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause; + function createImportClause(name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, isTypeOnly?: boolean): ImportClause; + function updateImportClause(node: ImportClause, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined, isTypeOnly: boolean): ImportClause; function createNamespaceImport(name: Identifier): NamespaceImport; function createNamespaceExport(name: Identifier): NamespaceExport; function updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport; @@ -4188,8 +4197,8 @@ declare namespace ts { function updateImportSpecifier(node: ImportSpecifier, propertyName: Identifier | undefined, name: Identifier): ImportSpecifier; function createExportAssignment(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, isExportEquals: boolean | undefined, expression: Expression): ExportAssignment; function updateExportAssignment(node: ExportAssignment, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, expression: Expression): ExportAssignment; - function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression): ExportDeclaration; - function updateExportDeclaration(node: ExportDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier: Expression | undefined): ExportDeclaration; + function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression, isTypeOnly?: boolean): ExportDeclaration; + function updateExportDeclaration(node: ExportDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier: Expression | undefined, isTypeOnly: boolean): ExportDeclaration; function createNamedExports(elements: readonly ExportSpecifier[]): NamedExports; function updateNamedExports(node: NamedExports, elements: readonly ExportSpecifier[]): NamedExports; function createExportSpecifier(propertyName: string | Identifier | undefined, name: string | Identifier): ExportSpecifier; diff --git a/tests/baselines/reference/awaitInNonAsyncFunction.errors.txt b/tests/baselines/reference/awaitInNonAsyncFunction.errors.txt index d7706ed107422..aa07a57b57ce2 100644 --- a/tests/baselines/reference/awaitInNonAsyncFunction.errors.txt +++ b/tests/baselines/reference/awaitInNonAsyncFunction.errors.txt @@ -13,7 +13,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(31,5): error TS1308: 'await' exp tests/cases/compiler/awaitInNonAsyncFunction.ts(34,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator. tests/cases/compiler/awaitInNonAsyncFunction.ts(35,5): error TS1308: 'await' expression is only allowed within an async function. tests/cases/compiler/awaitInNonAsyncFunction.ts(39,5): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator. -tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. +tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1375: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. ==== tests/cases/compiler/awaitInNonAsyncFunction.ts (16 errors) ==== @@ -100,4 +100,4 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1361: 'await' out !!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator. await null; ~~~~~ -!!! error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. \ No newline at end of file +!!! error TS1375: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. \ No newline at end of file diff --git a/tests/baselines/reference/chained.errors.txt b/tests/baselines/reference/chained.errors.txt new file mode 100644 index 0000000000000..8d684afbb9475 --- /dev/null +++ b/tests/baselines/reference/chained.errors.txt @@ -0,0 +1,27 @@ +/d.ts(2,5): error TS2693: 'D' only refers to a type, but is being used as a value here. +/d.ts(3,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 B }; + export type Z = A; + +==== /b.ts (0 errors) ==== + import { Z as Y } from './a'; + export { B as C } from './a'; + +==== /c.ts (0 errors) ==== + import type { C } from './b'; + export { C as D }; + +==== /d.ts (2 errors) ==== + import { D } from './c'; + new D(); + ~ +!!! error TS2693: 'D' only refers to a type, but is being used as a value here. + const d: D = {}; + ~ +!!! 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/chained.js b/tests/baselines/reference/chained.js new file mode 100644 index 0000000000000..e37d6361b2441 --- /dev/null +++ b/tests/baselines/reference/chained.js @@ -0,0 +1,40 @@ +//// [tests/cases/conformance/externalModules/typeOnly/chained.ts] //// + +//// [a.ts] +class A { a!: string } +export type { A as B }; +export type Z = A; + +//// [b.ts] +import { Z as Y } from './a'; +export { B as C } from './a'; + +//// [c.ts] +import type { C } from './b'; +export { C as D }; + +//// [d.ts] +import { D } from './c'; +new D(); +const d: D = {}; + + +//// [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; +//// [d.js] +"use strict"; +exports.__esModule = true; +new D(); +var d = {}; diff --git a/tests/baselines/reference/chained.symbols b/tests/baselines/reference/chained.symbols new file mode 100644 index 0000000000000..9ba6d5bed2142 --- /dev/null +++ b/tests/baselines/reference/chained.symbols @@ -0,0 +1,39 @@ +=== /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 B }; +>A : Symbol(A) +>B : Symbol(B, Decl(a.ts, 1, 13)) + +export type Z = A; +>Z : Symbol(Z, Decl(a.ts, 1, 23)) +>A : Symbol(A, Decl(a.ts, 0, 0)) + +=== /b.ts === +import { Z as Y } from './a'; +>Z : Symbol(Y, Decl(a.ts, 1, 23)) +>Y : Symbol(Y, Decl(b.ts, 0, 8)) + +export { B as C } from './a'; +>B : Symbol(B, Decl(a.ts, 1, 13)) +>C : Symbol(C, Decl(b.ts, 1, 8)) + +=== /c.ts === +import type { C } from './b'; +>C : Symbol(C, Decl(c.ts, 0, 13)) + +export { C as D }; +>C : Symbol(C, Decl(c.ts, 0, 13)) +>D : Symbol(D, Decl(c.ts, 1, 8)) + +=== /d.ts === +import { D } from './c'; +>D : Symbol(D, Decl(d.ts, 0, 8)) + +new D(); +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 new file mode 100644 index 0000000000000..5e52f608b821b --- /dev/null +++ b/tests/baselines/reference/chained.types @@ -0,0 +1,41 @@ +=== /a.ts === +class A { a!: string } +>A : A +>a : string + +export type { A as B }; +>A : A +>B : A + +export type Z = A; +>Z : A + +=== /b.ts === +import { Z as Y } from './a'; +>Z : any +>Y : any + +export { B as C } from './a'; +>B : any +>C : any + +=== /c.ts === +import type { C } from './b'; +>C : A + +export { C as D }; +>C : any +>D : any + +=== /d.ts === +import { D } from './c'; +>D : any + +new D(); +>new D() : any +>D : any + +const d: D = {}; +>d : A +>{} : {} + diff --git a/tests/baselines/reference/circular1.errors.txt b/tests/baselines/reference/circular1.errors.txt new file mode 100644 index 0000000000000..eb583371b3d72 --- /dev/null +++ b/tests/baselines/reference/circular1.errors.txt @@ -0,0 +1,11 @@ +/b.ts(1,15): error TS2303: Circular definition of import alias 'A'. + + +==== /a.ts (0 errors) ==== + export type { A } from './b'; + +==== /b.ts (1 errors) ==== + export type { A } from './a'; + ~ +!!! error TS2303: Circular definition of import alias 'A'. + \ No newline at end of file diff --git a/tests/baselines/reference/circular1.symbols b/tests/baselines/reference/circular1.symbols new file mode 100644 index 0000000000000..be98e672c1bcc --- /dev/null +++ b/tests/baselines/reference/circular1.symbols @@ -0,0 +1,8 @@ +=== /a.ts === +export type { A } from './b'; +>A : Symbol(A, Decl(a.ts, 0, 13)) + +=== /b.ts === +export type { A } from './a'; +>A : Symbol(A, Decl(b.ts, 0, 13)) + diff --git a/tests/baselines/reference/circular1.types b/tests/baselines/reference/circular1.types new file mode 100644 index 0000000000000..32a8ee27d8ce0 --- /dev/null +++ b/tests/baselines/reference/circular1.types @@ -0,0 +1,8 @@ +=== /a.ts === +export type { A } from './b'; +>A : any + +=== /b.ts === +export type { A } from './a'; +>A : any + diff --git a/tests/baselines/reference/circular2.errors.txt b/tests/baselines/reference/circular2.errors.txt new file mode 100644 index 0000000000000..9906fc8fa88e0 --- /dev/null +++ b/tests/baselines/reference/circular2.errors.txt @@ -0,0 +1,16 @@ +/a.ts(2,13): error TS2456: Type alias 'A' circularly references itself. +/b.ts(2,13): error TS2456: Type alias 'B' circularly references itself. + + +==== /a.ts (1 errors) ==== + import type { B } from './b'; + export type A = B; + ~ +!!! error TS2456: Type alias 'A' circularly references itself. + +==== /b.ts (1 errors) ==== + import type { A } from './a'; + export type B = A; + ~ +!!! error TS2456: Type alias 'B' circularly references itself. + \ No newline at end of file diff --git a/tests/baselines/reference/circular2.symbols b/tests/baselines/reference/circular2.symbols new file mode 100644 index 0000000000000..1303d78e0a6a6 --- /dev/null +++ b/tests/baselines/reference/circular2.symbols @@ -0,0 +1,16 @@ +=== /a.ts === +import type { B } from './b'; +>B : Symbol(B, Decl(a.ts, 0, 13)) + +export type A = B; +>A : Symbol(A, Decl(a.ts, 0, 29)) +>B : Symbol(B, Decl(a.ts, 0, 13)) + +=== /b.ts === +import type { A } from './a'; +>A : Symbol(A, Decl(b.ts, 0, 13)) + +export type B = A; +>B : Symbol(B, Decl(b.ts, 0, 29)) +>A : Symbol(A, Decl(b.ts, 0, 13)) + diff --git a/tests/baselines/reference/circular2.types b/tests/baselines/reference/circular2.types new file mode 100644 index 0000000000000..cea0b396bd042 --- /dev/null +++ b/tests/baselines/reference/circular2.types @@ -0,0 +1,14 @@ +=== /a.ts === +import type { B } from './b'; +>B : any + +export type A = B; +>A : any + +=== /b.ts === +import type { A } from './a'; +>A : any + +export type B = A; +>B : any + diff --git a/tests/baselines/reference/circular3.errors.txt b/tests/baselines/reference/circular3.errors.txt new file mode 100644 index 0000000000000..5da3e364065b2 --- /dev/null +++ b/tests/baselines/reference/circular3.errors.txt @@ -0,0 +1,13 @@ +/b.ts(1,15): error TS2303: Circular definition of import alias 'B'. + + +==== /a.ts (0 errors) ==== + import type { A } from './b'; + export type { A as B }; + +==== /b.ts (1 errors) ==== + import type { B } from './a'; + ~ +!!! error TS2303: Circular definition of import alias 'B'. + export type { B as A }; + \ No newline at end of file diff --git a/tests/baselines/reference/circular3.symbols b/tests/baselines/reference/circular3.symbols new file mode 100644 index 0000000000000..6cdec14d30cbd --- /dev/null +++ b/tests/baselines/reference/circular3.symbols @@ -0,0 +1,16 @@ +=== /a.ts === +import type { A } from './b'; +>A : Symbol(A, Decl(a.ts, 0, 13)) + +export type { A as B }; +>A : Symbol(A, Decl(a.ts, 0, 13)) +>B : Symbol(B, Decl(a.ts, 1, 13)) + +=== /b.ts === +import type { B } from './a'; +>B : Symbol(B, Decl(b.ts, 0, 13)) + +export type { B as A }; +>B : Symbol(B, Decl(b.ts, 0, 13)) +>A : Symbol(A, Decl(b.ts, 1, 13)) + diff --git a/tests/baselines/reference/circular3.types b/tests/baselines/reference/circular3.types new file mode 100644 index 0000000000000..1c63897fba59d --- /dev/null +++ b/tests/baselines/reference/circular3.types @@ -0,0 +1,16 @@ +=== /a.ts === +import type { A } from './b'; +>A : any + +export type { A as B }; +>A : any +>B : any + +=== /b.ts === +import type { B } from './a'; +>B : any + +export type { B as A }; +>B : any +>A : any + diff --git a/tests/baselines/reference/circular4.errors.txt b/tests/baselines/reference/circular4.errors.txt new file mode 100644 index 0000000000000..b55cab7bf7c56 --- /dev/null +++ b/tests/baselines/reference/circular4.errors.txt @@ -0,0 +1,24 @@ +/a.ts(4,17): error TS2456: Type alias 'T' circularly references itself. +/b.ts(4,17): error TS2456: Type alias 'T' circularly references itself. + + +==== /a.ts (1 errors) ==== + import type { ns2 } from './b'; + export namespace ns1 { + export namespace nested { + export type T = ns2.nested.T; + ~ +!!! error TS2456: Type alias 'T' circularly references itself. + } + } + +==== /b.ts (1 errors) ==== + import type { ns1 } from './a'; + export namespace ns2 { + export namespace nested { + export type T = ns1.nested.T; + ~ +!!! error TS2456: Type alias 'T' circularly references itself. + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/circular4.symbols b/tests/baselines/reference/circular4.symbols new file mode 100644 index 0000000000000..980ac1720f256 --- /dev/null +++ b/tests/baselines/reference/circular4.symbols @@ -0,0 +1,36 @@ +=== /a.ts === +import type { ns2 } from './b'; +>ns2 : Symbol(ns2, Decl(a.ts, 0, 13)) + +export namespace ns1 { +>ns1 : Symbol(ns1, Decl(a.ts, 0, 31)) + + export namespace nested { +>nested : Symbol(nested, Decl(a.ts, 1, 22)) + + export type T = ns2.nested.T; +>T : Symbol(T, Decl(a.ts, 2, 27)) +>ns2 : Symbol(ns2, Decl(a.ts, 0, 13)) +>nested : Symbol(ns2.nested, Decl(b.ts, 1, 22)) +>T : Symbol(ns2.nested.T, Decl(b.ts, 2, 27)) + } +} + +=== /b.ts === +import type { ns1 } from './a'; +>ns1 : Symbol(ns1, Decl(b.ts, 0, 13)) + +export namespace ns2 { +>ns2 : Symbol(ns2, Decl(b.ts, 0, 31)) + + export namespace nested { +>nested : Symbol(nested, Decl(b.ts, 1, 22)) + + export type T = ns1.nested.T; +>T : Symbol(T, Decl(b.ts, 2, 27)) +>ns1 : Symbol(ns1, Decl(b.ts, 0, 13)) +>nested : Symbol(ns1.nested, Decl(a.ts, 1, 22)) +>T : Symbol(ns1.nested.T, Decl(a.ts, 2, 27)) + } +} + diff --git a/tests/baselines/reference/circular4.types b/tests/baselines/reference/circular4.types new file mode 100644 index 0000000000000..491c36e6c16b1 --- /dev/null +++ b/tests/baselines/reference/circular4.types @@ -0,0 +1,26 @@ +=== /a.ts === +import type { ns2 } from './b'; +>ns2 : any + +export namespace ns1 { + export namespace nested { + export type T = ns2.nested.T; +>T : any +>ns2 : any +>nested : any + } +} + +=== /b.ts === +import type { ns1 } from './a'; +>ns1 : any + +export namespace ns2 { + export namespace nested { + export type T = ns1.nested.T; +>T : any +>ns1 : any +>nested : any + } +} + diff --git a/tests/baselines/reference/enums.errors.txt b/tests/baselines/reference/enums.errors.txt new file mode 100644 index 0000000000000..4e874cbee48dc --- /dev/null +++ b/tests/baselines/reference/enums.errors.txt @@ -0,0 +1,38 @@ +/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. + + +==== /a.ts (0 errors) ==== + enum SyntaxKind { + ImportClause, + ExportDeclaration + } + + const enum SymbolFlags { + Type = "Type", + Value = "Value" + } + + export type { SyntaxKind }; + export { SymbolFlags }; + +==== /b.ts (2 errors) ==== + import type { SyntaxKind, SymbolFlags } from './a'; + + SyntaxKind.ImportClause; + ~~~~~~~~~~ +!!! error TS1362: Enum 'SyntaxKind' cannot be used as a value because only its type has been imported. + SymbolFlags.Type; + ~~~~~~~~~~~ +!!! error TS1362: Enum 'SymbolFlags' cannot be used as a value because only its type has been imported. + let kind: SyntaxKind.ImportClause; + let flags: SymbolFlags; + + type TypeFlag = SymbolFlags.Type; + export type { TypeFlag }; + +==== /c.ts (0 errors) ==== + import { SymbolFlags } from './a'; + import type { TypeFlag } from './b'; + const flags: TypeFlag = SymbolFlags.Type; + \ No newline at end of file diff --git a/tests/baselines/reference/enums.js b/tests/baselines/reference/enums.js new file mode 100644 index 0000000000000..09243e4a7f134 --- /dev/null +++ b/tests/baselines/reference/enums.js @@ -0,0 +1,52 @@ +//// [tests/cases/conformance/externalModules/typeOnly/enums.ts] //// + +//// [a.ts] +enum SyntaxKind { + ImportClause, + ExportDeclaration +} + +const enum SymbolFlags { + Type = "Type", + Value = "Value" +} + +export type { SyntaxKind }; +export { SymbolFlags }; + +//// [b.ts] +import type { SyntaxKind, SymbolFlags } from './a'; + +SyntaxKind.ImportClause; +SymbolFlags.Type; +let kind: SyntaxKind.ImportClause; +let flags: SymbolFlags; + +type TypeFlag = SymbolFlags.Type; +export type { TypeFlag }; + +//// [c.ts] +import { SymbolFlags } from './a'; +import type { TypeFlag } from './b'; +const flags: TypeFlag = SymbolFlags.Type; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var SyntaxKind; +(function (SyntaxKind) { + SyntaxKind[SyntaxKind["ImportClause"] = 0] = "ImportClause"; + SyntaxKind[SyntaxKind["ExportDeclaration"] = 1] = "ExportDeclaration"; +})(SyntaxKind || (SyntaxKind = {})); +//// [b.js] +"use strict"; +exports.__esModule = true; +SyntaxKind.ImportClause; +SymbolFlags.Type; +var kind; +var flags; +//// [c.js] +"use strict"; +exports.__esModule = true; +var flags = "Type" /* Type */; diff --git a/tests/baselines/reference/enums.symbols b/tests/baselines/reference/enums.symbols new file mode 100644 index 0000000000000..edac3617acbee --- /dev/null +++ b/tests/baselines/reference/enums.symbols @@ -0,0 +1,65 @@ +=== /a.ts === +enum SyntaxKind { +>SyntaxKind : Symbol(SyntaxKind, Decl(a.ts, 0, 0)) + + ImportClause, +>ImportClause : Symbol(SyntaxKind.ImportClause, Decl(a.ts, 0, 17)) + + ExportDeclaration +>ExportDeclaration : Symbol(SyntaxKind.ExportDeclaration, Decl(a.ts, 1, 15)) +} + +const enum SymbolFlags { +>SymbolFlags : Symbol(SymbolFlags, Decl(a.ts, 3, 1)) + + Type = "Type", +>Type : Symbol(SymbolFlags.Type, Decl(a.ts, 5, 24)) + + Value = "Value" +>Value : Symbol(SymbolFlags.Value, Decl(a.ts, 6, 16)) +} + +export type { SyntaxKind }; +>SyntaxKind : Symbol(SyntaxKind, Decl(a.ts, 10, 13)) + +export { SymbolFlags }; +>SymbolFlags : Symbol(SymbolFlags, Decl(a.ts, 11, 8)) + +=== /b.ts === +import type { SyntaxKind, SymbolFlags } from './a'; +>SyntaxKind : Symbol(SyntaxKind, Decl(b.ts, 0, 13)) +>SymbolFlags : Symbol(SymbolFlags, Decl(b.ts, 0, 25)) + +SyntaxKind.ImportClause; +SymbolFlags.Type; +let kind: SyntaxKind.ImportClause; +>kind : Symbol(kind, Decl(b.ts, 4, 3)) +>SyntaxKind : Symbol(SyntaxKind, Decl(b.ts, 0, 13)) +>ImportClause : Symbol(SyntaxKind.ImportClause) + +let flags: SymbolFlags; +>flags : Symbol(flags, Decl(b.ts, 5, 3)) +>SymbolFlags : Symbol(SymbolFlags, Decl(b.ts, 0, 25)) + +type TypeFlag = SymbolFlags.Type; +>TypeFlag : Symbol(TypeFlag, Decl(b.ts, 5, 23)) +>SymbolFlags : Symbol(SymbolFlags, Decl(b.ts, 0, 25)) +>Type : Symbol(SymbolFlags.Type) + +export type { TypeFlag }; +>TypeFlag : Symbol(TypeFlag, Decl(b.ts, 8, 13)) + +=== /c.ts === +import { SymbolFlags } from './a'; +>SymbolFlags : Symbol(SymbolFlags, Decl(c.ts, 0, 8)) + +import type { TypeFlag } from './b'; +>TypeFlag : Symbol(TypeFlag, Decl(c.ts, 1, 13)) + +const flags: TypeFlag = SymbolFlags.Type; +>flags : Symbol(flags, Decl(c.ts, 2, 5)) +>TypeFlag : Symbol(TypeFlag, Decl(c.ts, 1, 13)) +>SymbolFlags.Type : Symbol(SymbolFlags.Type, Decl(a.ts, 5, 24)) +>SymbolFlags : Symbol(SymbolFlags, Decl(c.ts, 0, 8)) +>Type : Symbol(SymbolFlags.Type, Decl(a.ts, 5, 24)) + diff --git a/tests/baselines/reference/enums.types b/tests/baselines/reference/enums.types new file mode 100644 index 0000000000000..ca79cc6147d4a --- /dev/null +++ b/tests/baselines/reference/enums.types @@ -0,0 +1,71 @@ +=== /a.ts === +enum SyntaxKind { +>SyntaxKind : SyntaxKind + + ImportClause, +>ImportClause : SyntaxKind.ImportClause + + ExportDeclaration +>ExportDeclaration : SyntaxKind.ExportDeclaration +} + +const enum SymbolFlags { +>SymbolFlags : SymbolFlags + + Type = "Type", +>Type : SymbolFlags.Type +>"Type" : "Type" + + Value = "Value" +>Value : SymbolFlags.Value +>"Value" : "Value" +} + +export type { SyntaxKind }; +>SyntaxKind : SyntaxKind + +export { SymbolFlags }; +>SymbolFlags : typeof SymbolFlags + +=== /b.ts === +import type { SyntaxKind, SymbolFlags } from './a'; +>SyntaxKind : SyntaxKind +>SymbolFlags : import("/a").SymbolFlags + +SyntaxKind.ImportClause; +>SyntaxKind.ImportClause : any +>SyntaxKind : any +>ImportClause : any + +SymbolFlags.Type; +>SymbolFlags.Type : any +>SymbolFlags : any +>Type : any + +let kind: SyntaxKind.ImportClause; +>kind : SyntaxKind.ImportClause +>SyntaxKind : any + +let flags: SymbolFlags; +>flags : import("/a").SymbolFlags + +type TypeFlag = SymbolFlags.Type; +>TypeFlag : import("/a").SymbolFlags.Type +>SymbolFlags : any + +export type { TypeFlag }; +>TypeFlag : import("/a").SymbolFlags.Type + +=== /c.ts === +import { SymbolFlags } from './a'; +>SymbolFlags : typeof SymbolFlags + +import type { TypeFlag } from './b'; +>TypeFlag : SymbolFlags.Type + +const flags: TypeFlag = SymbolFlags.Type; +>flags : SymbolFlags.Type +>SymbolFlags.Type : SymbolFlags.Type +>SymbolFlags : typeof SymbolFlags +>Type : SymbolFlags.Type + diff --git a/tests/baselines/reference/exportDeclaration.errors.txt b/tests/baselines/reference/exportDeclaration.errors.txt new file mode 100644 index 0000000000000..387b348843945 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration.errors.txt @@ -0,0 +1,14 @@ +/b.ts(3,5): error TS2693: 'A' only refers to a type, but is being used as a value here. + + +==== /a.ts (0 errors) ==== + class A {} + export type { A }; + +==== /b.ts (1 errors) ==== + import { A } from './a'; + declare const a: A; + new A(); + ~ +!!! error TS2693: 'A' only refers to a type, but is being used as a value here. + \ No newline at end of file diff --git a/tests/baselines/reference/exportDeclaration.js b/tests/baselines/reference/exportDeclaration.js new file mode 100644 index 0000000000000..2699093d1323d --- /dev/null +++ b/tests/baselines/reference/exportDeclaration.js @@ -0,0 +1,24 @@ +//// [tests/cases/conformance/externalModules/typeOnly/exportDeclaration.ts] //// + +//// [a.ts] +class A {} +export type { A }; + +//// [b.ts] +import { A } from './a'; +declare const a: A; +new A(); + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +//// [b.js] +"use strict"; +exports.__esModule = true; +new A(); diff --git a/tests/baselines/reference/exportDeclaration.symbols b/tests/baselines/reference/exportDeclaration.symbols new file mode 100644 index 0000000000000..c35f92e71592e --- /dev/null +++ b/tests/baselines/reference/exportDeclaration.symbols @@ -0,0 +1,17 @@ +=== /a.ts === +class A {} +>A : Symbol(A, Decl(a.ts, 0, 0)) + +export type { A }; +>A : Symbol(A, Decl(a.ts, 1, 13)) + +=== /b.ts === +import { A } from './a'; +>A : Symbol(A, Decl(b.ts, 0, 8)) + +declare const a: A; +>a : Symbol(a, Decl(b.ts, 1, 13)) +>A : Symbol(A, Decl(b.ts, 0, 8)) + +new A(); + diff --git a/tests/baselines/reference/exportDeclaration.types b/tests/baselines/reference/exportDeclaration.types new file mode 100644 index 0000000000000..277082b8586a8 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration.types @@ -0,0 +1,18 @@ +=== /a.ts === +class A {} +>A : A + +export type { A }; +>A : A + +=== /b.ts === +import { A } from './a'; +>A : any + +declare const a: A; +>a : A + +new A(); +>new A() : any +>A : any + diff --git a/tests/baselines/reference/exportDeclaration_missingBraces.errors.txt b/tests/baselines/reference/exportDeclaration_missingBraces.errors.txt new file mode 100644 index 0000000000000..9e74a9eea8b78 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_missingBraces.errors.txt @@ -0,0 +1,57 @@ +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(1,6): error TS2567: Enum declarations can only merge with namespace or other enum declarations. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(6,7): error TS2300: Duplicate identifier 'Box'. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(8,11): error TS2300: Duplicate identifier 'Circle'. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(11,16): error TS1005: '=' expected. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(14,13): error TS2567: Enum declarations can only merge with namespace or other enum declarations. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(14,20): error TS1005: '=' expected. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(15,13): error TS2300: Duplicate identifier 'Box'. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(15,16): error TS1005: '=' expected. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(16,13): error TS2300: Duplicate identifier 'Circle'. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(16,19): error TS1005: '=' expected. +tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts(17,15): error TS1005: '=' expected. + + +==== tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts (11 errors) ==== + enum Numbers { + ~~~~~~~ +!!! error TS2567: Enum declarations can only merge with namespace or other enum declarations. + One, + Two + } + + class Box {} + ~~~ +!!! error TS2300: Duplicate identifier 'Box'. + + interface Circle {} + ~~~~~~ +!!! error TS2300: Duplicate identifier 'Circle'. + + namespace ns { + export type T; // Normal parse error because there is no other 'T' + ~ +!!! error TS1005: '=' expected. + } + + export type Numbers; + ~~~~~~~ +!!! error TS2567: Enum declarations can only merge with namespace or other enum declarations. +!!! related TS1369 tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts:14:13: Did you mean 'export type { Numbers }'? + ~ +!!! error TS1005: '=' expected. + export type Box; + ~~~ +!!! error TS2300: Duplicate identifier 'Box'. +!!! related TS1369 tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts:15:13: Did you mean 'export type { Box }'? + ~ +!!! error TS1005: '=' expected. + export type Circle; + ~~~~~~ +!!! error TS2300: Duplicate identifier 'Circle'. +!!! related TS1369 tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts:16:13: Did you mean 'export type { Circle }'? + ~ +!!! error TS1005: '=' expected. + export type ns; + ~ +!!! error TS1005: '=' expected. + \ No newline at end of file diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.js b/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.js new file mode 100644 index 0000000000000..2915e1f0eb8b7 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.js @@ -0,0 +1,15 @@ +//// [tests/cases/conformance/externalModules/typeOnly/exportDeclaration_moduleSpecifier-isolatedModules.ts] //// + +//// [a.ts] +export type A = {}; + +//// [b.ts] +export type { A } from './a'; // should not error, but would without `type` + + +//// [a.js] +"use strict"; +exports.__esModule = true; +//// [b.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.symbols b/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.symbols new file mode 100644 index 0000000000000..17a4593a0e9bf --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.symbols @@ -0,0 +1,8 @@ +=== /a.ts === +export type A = {}; +>A : Symbol(A, Decl(a.ts, 0, 0)) + +=== /b.ts === +export type { A } from './a'; // should not error, but would without `type` +>A : Symbol(A, Decl(b.ts, 0, 13)) + diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.types b/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.types new file mode 100644 index 0000000000000..78d90f12023c3 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier-isolatedModules.types @@ -0,0 +1,8 @@ +=== /a.ts === +export type A = {}; +>A : A + +=== /b.ts === +export type { A } from './a'; // should not error, but would without `type` +>A : import("/a").A + diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier.errors.txt b/tests/baselines/reference/exportDeclaration_moduleSpecifier.errors.txt new file mode 100644 index 0000000000000..6ef18aad95986 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier.errors.txt @@ -0,0 +1,16 @@ +/c.ts(3,5): error TS2693: 'A' only refers to a type, but is being used as a value here. + + +==== /a.ts (0 errors) ==== + export class A {} + +==== /b.ts (0 errors) ==== + export type { A } from './a'; + +==== /c.ts (1 errors) ==== + import { A } from './b'; + declare const a: A; + new A(); + ~ +!!! error TS2693: 'A' only refers to a type, but is being used as a value here. + \ No newline at end of file diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier.js b/tests/baselines/reference/exportDeclaration_moduleSpecifier.js new file mode 100644 index 0000000000000..edc093f700b89 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier.js @@ -0,0 +1,30 @@ +//// [tests/cases/conformance/externalModules/typeOnly/exportDeclaration_moduleSpecifier.ts] //// + +//// [a.ts] +export class A {} + +//// [b.ts] +export type { A } from './a'; + +//// [c.ts] +import { A } from './b'; +declare const a: A; +new 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; +//// [c.js] +"use strict"; +exports.__esModule = true; +new A(); diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier.symbols b/tests/baselines/reference/exportDeclaration_moduleSpecifier.symbols new file mode 100644 index 0000000000000..e4917cddc066d --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier.symbols @@ -0,0 +1,18 @@ +=== /a.ts === +export class A {} +>A : Symbol(A, Decl(a.ts, 0, 0)) + +=== /b.ts === +export type { A } from './a'; +>A : Symbol(A, Decl(b.ts, 0, 13)) + +=== /c.ts === +import { A } from './b'; +>A : Symbol(A, Decl(c.ts, 0, 8)) + +declare const a: A; +>a : Symbol(a, Decl(c.ts, 1, 13)) +>A : Symbol(A, Decl(c.ts, 0, 8)) + +new A(); + diff --git a/tests/baselines/reference/exportDeclaration_moduleSpecifier.types b/tests/baselines/reference/exportDeclaration_moduleSpecifier.types new file mode 100644 index 0000000000000..b870b2804f582 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_moduleSpecifier.types @@ -0,0 +1,19 @@ +=== /a.ts === +export class A {} +>A : A + +=== /b.ts === +export type { A } from './a'; +>A : import("/a").A + +=== /c.ts === +import { A } from './b'; +>A : any + +declare const a: A; +>a : import("/a").A + +new A(); +>new A() : any +>A : any + diff --git a/tests/baselines/reference/exportDeclaration_value.errors.txt b/tests/baselines/reference/exportDeclaration_value.errors.txt new file mode 100644 index 0000000000000..d759ad8c7c57b --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_value.errors.txt @@ -0,0 +1,18 @@ +/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/exportDeclaration_value.js b/tests/baselines/reference/exportDeclaration_value.js new file mode 100644 index 0000000000000..8abeef1a7b279 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_value.js @@ -0,0 +1,19 @@ +//// [tests/cases/conformance/externalModules/typeOnly/exportDeclaration_value.ts] //// + +//// [a.ts] +const A = {}; +export type { A }; +export const AA = {}; + +//// [b.ts] +export type { AA } from './a'; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = {}; +exports.AA = {}; +//// [b.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/exportDeclaration_value.symbols b/tests/baselines/reference/exportDeclaration_value.symbols new file mode 100644 index 0000000000000..505dba2c5591b --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_value.symbols @@ -0,0 +1,14 @@ +=== /a.ts === +const A = {}; +>A : Symbol(A, Decl(a.ts, 0, 5)) + +export type { A }; +>A : Symbol(A, Decl(a.ts, 1, 13)) + +export const AA = {}; +>AA : Symbol(AA, Decl(a.ts, 2, 12)) + +=== /b.ts === +export type { AA } from './a'; +>AA : Symbol(AA, Decl(b.ts, 0, 13)) + diff --git a/tests/baselines/reference/exportDeclaration_value.types b/tests/baselines/reference/exportDeclaration_value.types new file mode 100644 index 0000000000000..524b6fd57fb21 --- /dev/null +++ b/tests/baselines/reference/exportDeclaration_value.types @@ -0,0 +1,16 @@ +=== /a.ts === +const A = {}; +>A : {} +>{} : {} + +export type { A }; +>A : any + +export const AA = {}; +>AA : {} +>{} : {} + +=== /b.ts === +export type { AA } from './a'; +>AA : any + diff --git a/tests/baselines/reference/filterNamespace_import.errors.txt b/tests/baselines/reference/filterNamespace_import.errors.txt new file mode 100644 index 0000000000000..b9e9a09f8c790 --- /dev/null +++ b/tests/baselines/reference/filterNamespace_import.errors.txt @@ -0,0 +1,30 @@ +/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 new file mode 100644 index 0000000000000..094cabd326760 --- /dev/null +++ b/tests/baselines/reference/filterNamespace_import.js @@ -0,0 +1,56 @@ +//// [tests/cases/conformance/externalModules/typeOnly/filterNamespace_import.ts] //// + +//// [ns.ts] +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] +import type ns from './ns'; +ns.Class; // Error +ns.Value; // Error +let c: ns.Class; +let t: ns.Type = ""; +let n: ns.nested.NestedClass = { a: '' }; + + +//// [ns.js] +"use strict"; +exports.__esModule = true; +var ns; +(function (ns) { + var Class = /** @class */ (function () { + function Class() { + } + return Class; + }()); + ns.Class = Class; + ns.Value = ""; + var nested; + (function (nested) { + var NestedClass = /** @class */ (function () { + function NestedClass() { + } + return NestedClass; + }()); + nested.NestedClass = NestedClass; + })(nested = ns.nested || (ns.nested = {})); +})(ns || (ns = {})); +exports["default"] = ns; +//// [a.js] +"use strict"; +exports.__esModule = true; +ns.Class; // Error +ns.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 new file mode 100644 index 0000000000000..f38ea7cc983c9 --- /dev/null +++ b/tests/baselines/reference/filterNamespace_import.symbols @@ -0,0 +1,51 @@ +=== /ns.ts === +namespace ns { +>ns : Symbol(ns, Decl(ns.ts, 0, 0)) + + export type Type = string; +>Type : Symbol(Type, Decl(ns.ts, 0, 14)) + + export class Class {} +>Class : Symbol(Class, Decl(ns.ts, 1, 28)) + + export const Value = ""; +>Value : Symbol(Value, Decl(ns.ts, 3, 14)) + + export namespace nested { +>nested : Symbol(nested, Decl(ns.ts, 3, 26)) + + export class NestedClass { +>NestedClass : Symbol(NestedClass, Decl(ns.ts, 4, 27)) + + a!: string; +>a : Symbol(NestedClass.a, Decl(ns.ts, 5, 30)) + } + } +} + +export default ns; +>ns : Symbol(ns, Decl(ns.ts, 0, 0)) + +=== /a.ts === +import type ns from './ns'; +>ns : Symbol(ns, Decl(a.ts, 0, 6)) + +ns.Class; // Error +ns.Value; // Error +let c: ns.Class; +>c : Symbol(c, Decl(a.ts, 3, 3)) +>ns : Symbol(ns, Decl(a.ts, 0, 6)) +>Class : Symbol(ns.Class) + +let t: ns.Type = ""; +>t : Symbol(t, Decl(a.ts, 4, 3)) +>ns : Symbol(ns, Decl(a.ts, 0, 6)) +>Type : Symbol(ns.Type, Decl(ns.ts, 0, 14)) + +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) +>a : Symbol(a, Decl(a.ts, 5, 32)) + diff --git a/tests/baselines/reference/filterNamespace_import.types b/tests/baselines/reference/filterNamespace_import.types new file mode 100644 index 0000000000000..ef9ad8a6e269b --- /dev/null +++ b/tests/baselines/reference/filterNamespace_import.types @@ -0,0 +1,60 @@ +=== /ns.ts === +namespace ns { +>ns : typeof ns + + export type Type = string; +>Type : string + + export class Class {} +>Class : Class + + export const Value = ""; +>Value : "" +>"" : "" + + export namespace nested { +>nested : typeof nested + + export class NestedClass { +>NestedClass : NestedClass + + a!: string; +>a : string + } + } +} + +export default ns; +>ns : typeof ns + +=== /a.ts === +import type ns from './ns'; +>ns : any + +ns.Class; // Error +>ns.Class : any +>ns : any +>Class : any + +ns.Value; // Error +>ns.Value : any +>ns : any +>Value : any + +let c: ns.Class; +>c : import("/ns").default.Class +>ns : any + +let t: ns.Type = ""; +>t : string +>ns : any +>"" : "" + +let n: ns.nested.NestedClass = { a: '' }; +>n : import("/ns").default.nested.NestedClass +>ns : any +>nested : any +>{ a: '' } : { a: string; } +>a : string +>'' : "" + diff --git a/tests/baselines/reference/generic.errors.txt b/tests/baselines/reference/generic.errors.txt new file mode 100644 index 0000000000000..0b1c394dec78d --- /dev/null +++ b/tests/baselines/reference/generic.errors.txt @@ -0,0 +1,20 @@ +/b.ts(5,5): error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. +/b.ts(6,8): error TS2314: Generic type 'A' requires 1 type argument(s). + + +==== /a.ts (0 errors) ==== + export class A { a!: T } + export type { A as B }; + +==== /b.ts (2 errors) ==== + import type { A } from './a'; + import { B } from './a'; + let a: A = { a: "" }; + let b: B = { a: 3 }; + let c: A = {}; + ~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. +!!! related TS2728 /a.ts:1:21: 'a' is declared here. + let d: B = { a: "" }; + ~ +!!! error TS2314: Generic type 'A' requires 1 type argument(s). \ No newline at end of file diff --git a/tests/baselines/reference/generic.js b/tests/baselines/reference/generic.js new file mode 100644 index 0000000000000..ba9093b688330 --- /dev/null +++ b/tests/baselines/reference/generic.js @@ -0,0 +1,30 @@ +//// [tests/cases/conformance/externalModules/typeOnly/generic.ts] //// + +//// [a.ts] +export class A { a!: T } +export type { A as B }; + +//// [b.ts] +import type { A } from './a'; +import { B } from './a'; +let a: A = { a: "" }; +let b: B = { a: 3 }; +let c: A = {}; +let d: B = { 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 a = { a: "" }; +var b = { a: 3 }; +var c = {}; +var d = { a: "" }; diff --git a/tests/baselines/reference/generic.symbols b/tests/baselines/reference/generic.symbols new file mode 100644 index 0000000000000..88625de90732d --- /dev/null +++ b/tests/baselines/reference/generic.symbols @@ -0,0 +1,37 @@ +=== /a.ts === +export class A { a!: T } +>A : Symbol(A, Decl(a.ts, 0, 0)) +>T : Symbol(T, Decl(a.ts, 0, 15)) +>a : Symbol(A.a, Decl(a.ts, 0, 19)) +>T : Symbol(T, Decl(a.ts, 0, 15)) + +export type { A as B }; +>A : Symbol(A) +>B : Symbol(B, Decl(a.ts, 1, 13)) + +=== /b.ts === +import type { A } from './a'; +>A : Symbol(A, Decl(b.ts, 0, 13)) + +import { B } from './a'; +>B : Symbol(B, Decl(b.ts, 1, 8)) + +let a: A = { a: "" }; +>a : Symbol(a, Decl(b.ts, 2, 3)) +>A : Symbol(A, Decl(b.ts, 0, 13)) +>a : Symbol(a, Decl(b.ts, 2, 20)) + +let b: B = { a: 3 }; +>b : Symbol(b, Decl(b.ts, 3, 3)) +>B : Symbol(B, Decl(b.ts, 1, 8)) +>a : Symbol(a, Decl(b.ts, 3, 20)) + +let c: A = {}; +>c : Symbol(c, Decl(b.ts, 4, 3)) +>A : Symbol(A, Decl(b.ts, 0, 13)) + +let d: B = { a: "" }; +>d : Symbol(d, Decl(b.ts, 5, 3)) +>B : Symbol(B, Decl(b.ts, 1, 8)) +>a : Symbol(a, Decl(b.ts, 5, 12)) + diff --git a/tests/baselines/reference/generic.types b/tests/baselines/reference/generic.types new file mode 100644 index 0000000000000..abe6f1da4a0a5 --- /dev/null +++ b/tests/baselines/reference/generic.types @@ -0,0 +1,38 @@ +=== /a.ts === +export class A { a!: T } +>A : A +>a : T + +export type { A as B }; +>A : A +>B : A + +=== /b.ts === +import type { A } from './a'; +>A : import("/a").A + +import { B } from './a'; +>B : any + +let a: A = { a: "" }; +>a : import("/a").A +>{ a: "" } : { a: string; } +>a : string +>"" : "" + +let b: B = { a: 3 }; +>b : import("/a").A +>{ a: 3 } : { a: number; } +>a : number +>3 : 3 + +let c: A = {}; +>c : import("/a").A +>{} : {} + +let d: B = { a: "" }; +>d : any +>{ a: "" } : { a: string; } +>a : string +>"" : "" + diff --git a/tests/baselines/reference/grammarErrors.errors.txt b/tests/baselines/reference/grammarErrors.errors.txt new file mode 100644 index 0000000000000..ad9d7c9f859ff --- /dev/null +++ b/tests/baselines/reference/grammarErrors.errors.txt @@ -0,0 +1,29 @@ +error TS5055: Cannot write file '/a.js' because it would overwrite input file. + Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig. +error TS5056: Cannot write file '/a.js' because it would be overwritten by multiple input files. +/a.js(1,1): error TS8006: 'import type' declarations can only be used in TypeScript files. +/a.js(2,1): error TS8006: 'export type' declarations can only be used in TypeScript files. +/b.ts(1,8): error TS1363: A type-only import can specify a default import or named bindings, but not both. + + +!!! error TS5055: Cannot write file '/a.js' because it would overwrite input file. +!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig. +!!! error TS5056: Cannot write file '/a.js' because it would be overwritten by multiple input files. +==== /a.ts (0 errors) ==== + export default class A {} + export class B {} + export class C {} + +==== /b.ts (1 errors) ==== + import type A, { B, C } from './a'; + ~~~~~~~~~~~~~~~~ +!!! error TS1363: A type-only import can specify a default import or named bindings, but not both. + +==== /a.js (2 errors) ==== + import type A from './a'; + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS8006: 'import type' declarations can only be used in TypeScript files. + export type { A }; + ~~~~~~~~~~~~~~~~~~ +!!! error TS8006: 'export type' declarations can only be used in TypeScript files. + \ No newline at end of file diff --git a/tests/baselines/reference/grammarErrors.js b/tests/baselines/reference/grammarErrors.js new file mode 100644 index 0000000000000..bb1e4e1d1ec55 --- /dev/null +++ b/tests/baselines/reference/grammarErrors.js @@ -0,0 +1,18 @@ +//// [tests/cases/conformance/externalModules/typeOnly/grammarErrors.ts] //// + +//// [a.ts] +export default class A {} +export class B {} +export class C {} + +//// [b.ts] +import type A, { B, C } from './a'; + +//// [a.js] +import type A from './a'; +export type { A }; + + +//// [b.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/importClause_default.errors.txt b/tests/baselines/reference/importClause_default.errors.txt new file mode 100644 index 0000000000000..99345c4102f4c --- /dev/null +++ b/tests/baselines/reference/importClause_default.errors.txt @@ -0,0 +1,13 @@ +/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 new file mode 100644 index 0000000000000..d9e7c54331d4f --- /dev/null +++ b/tests/baselines/reference/importClause_default.js @@ -0,0 +1,25 @@ +//// [tests/cases/conformance/externalModules/typeOnly/importClause_default.ts] //// + +//// [a.ts] +export default class A { a!: string } + +//// [b.ts] +import type A from './a'; +new A(); +let a: A = { a: '' }; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +exports["default"] = A; +//// [b.js] +"use strict"; +exports.__esModule = true; +new A(); +var a = { a: '' }; diff --git a/tests/baselines/reference/importClause_default.symbols b/tests/baselines/reference/importClause_default.symbols new file mode 100644 index 0000000000000..3c13a061eaa23 --- /dev/null +++ b/tests/baselines/reference/importClause_default.symbols @@ -0,0 +1,15 @@ +=== /a.ts === +export default class A { a!: string } +>A : Symbol(A, Decl(a.ts, 0, 0)) +>a : Symbol(A.a, Decl(a.ts, 0, 24)) + +=== /b.ts === +import type A from './a'; +>A : Symbol(A, Decl(b.ts, 0, 6)) + +new A(); +let a: A = { a: '' }; +>a : Symbol(a, Decl(b.ts, 2, 3)) +>A : Symbol(A, Decl(b.ts, 0, 6)) +>a : Symbol(a, Decl(b.ts, 2, 12)) + diff --git a/tests/baselines/reference/importClause_default.types b/tests/baselines/reference/importClause_default.types new file mode 100644 index 0000000000000..47ab1c72d607a --- /dev/null +++ b/tests/baselines/reference/importClause_default.types @@ -0,0 +1,19 @@ +=== /a.ts === +export default class A { a!: string } +>A : A +>a : string + +=== /b.ts === +import type A from './a'; +>A : import("/a").default + +new A(); +>new A() : any +>A : any + +let a: A = { a: '' }; +>a : import("/a").default +>{ a: '' } : { a: string; } +>a : string +>'' : "" + diff --git a/tests/baselines/reference/importClause_namedImports.errors.txt b/tests/baselines/reference/importClause_namedImports.errors.txt new file mode 100644 index 0000000000000..c5cc2bb326006 --- /dev/null +++ b/tests/baselines/reference/importClause_namedImports.errors.txt @@ -0,0 +1,21 @@ +/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. + + +==== /abc.ts (0 errors) ==== + export class A {} + export type B = { b: string }; + export const C = ""; + +==== /d.ts (2 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. + declare let a: A; + declare let b: B; + b.b; + \ No newline at end of file diff --git a/tests/baselines/reference/importClause_namedImports.js b/tests/baselines/reference/importClause_namedImports.js new file mode 100644 index 0000000000000..af9017f73130d --- /dev/null +++ b/tests/baselines/reference/importClause_namedImports.js @@ -0,0 +1,30 @@ +//// [tests/cases/conformance/externalModules/typeOnly/importClause_namedImports.ts] //// + +//// [abc.ts] +export class A {} +export type B = { b: string }; +export const C = ""; + +//// [d.ts] +import type { A, B, C } from './abc'; +new A(); +declare let a: A; +declare let b: B; +b.b; + + +//// [abc.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +exports.A = A; +exports.C = ""; +//// [d.js] +"use strict"; +exports.__esModule = true; +new A(); +b.b; diff --git a/tests/baselines/reference/importClause_namedImports.symbols b/tests/baselines/reference/importClause_namedImports.symbols new file mode 100644 index 0000000000000..a21e8d8cba2bb --- /dev/null +++ b/tests/baselines/reference/importClause_namedImports.symbols @@ -0,0 +1,31 @@ +=== /abc.ts === +export class A {} +>A : Symbol(A, Decl(abc.ts, 0, 0)) + +export type B = { b: string }; +>B : Symbol(B, Decl(abc.ts, 0, 17)) +>b : Symbol(b, Decl(abc.ts, 1, 18)) + +export const C = ""; +>C : Symbol(C, Decl(abc.ts, 2, 12)) + +=== /d.ts === +import type { A, B, C } from './abc'; +>A : Symbol(A, Decl(d.ts, 0, 13)) +>B : Symbol(B, Decl(d.ts, 0, 16)) +>C : Symbol(C, Decl(d.ts, 0, 19)) + +new A(); +declare let a: A; +>a : Symbol(a, Decl(d.ts, 2, 11)) +>A : Symbol(A, Decl(d.ts, 0, 13)) + +declare let b: B; +>b : Symbol(b, Decl(d.ts, 3, 11)) +>B : Symbol(B, Decl(d.ts, 0, 16)) + +b.b; +>b.b : Symbol(b, Decl(abc.ts, 1, 18)) +>b : Symbol(b, Decl(d.ts, 3, 11)) +>b : Symbol(b, Decl(abc.ts, 1, 18)) + diff --git a/tests/baselines/reference/importClause_namedImports.types b/tests/baselines/reference/importClause_namedImports.types new file mode 100644 index 0000000000000..a9c43fcf582ad --- /dev/null +++ b/tests/baselines/reference/importClause_namedImports.types @@ -0,0 +1,33 @@ +=== /abc.ts === +export class A {} +>A : A + +export type B = { b: string }; +>B : B +>b : string + +export const C = ""; +>C : "" +>"" : "" + +=== /d.ts === +import type { A, B, C } from './abc'; +>A : import("/abc").A +>B : B +>C : any + +new A(); +>new A() : any +>A : any + +declare let a: A; +>a : import("/abc").A + +declare let b: B; +>b : B + +b.b; +>b.b : string +>b : B +>b : string + diff --git a/tests/baselines/reference/importClause_namespaceImport.errors.txt b/tests/baselines/reference/importClause_namespaceImport.errors.txt new file mode 100644 index 0000000000000..04d8dceddd02c --- /dev/null +++ b/tests/baselines/reference/importClause_namespaceImport.errors.txt @@ -0,0 +1,34 @@ +/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'. + + +==== /a.ts (0 errors) ==== + export class A { a!: string } + export class B { b!: number } + export type C = T; + export const Value = {}; + +==== /b.ts (5 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'. + const a: types.A = {}; + ~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type 'A'. +!!! related TS2728 /a.ts:1:18: 'a' is declared here. + const b: types.B = {}; + ~ +!!! error TS2741: Property 'b' is missing in type '{}' but required in type 'B'. +!!! related TS2728 /a.ts:2:18: 'b' is declared here. + const c: types.C = ""; + \ No newline at end of file diff --git a/tests/baselines/reference/importClause_namespaceImport.js b/tests/baselines/reference/importClause_namespaceImport.js new file mode 100644 index 0000000000000..4e1410e999d86 --- /dev/null +++ b/tests/baselines/reference/importClause_namespaceImport.js @@ -0,0 +1,43 @@ +//// [tests/cases/conformance/externalModules/typeOnly/importClause_namespaceImport.ts] //// + +//// [a.ts] +export class A { a!: string } +export class B { b!: number } +export type C = T; +export const Value = {}; + +//// [b.ts] +import type * as types from './a'; +types; +types.Value; +let v: types.Value; +const a: types.A = {}; +const b: types.B = {}; +const c: types.C = ""; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +exports.A = A; +var B = /** @class */ (function () { + function B() { + } + return B; +}()); +exports.B = B; +exports.Value = {}; +//// [b.js] +"use strict"; +exports.__esModule = true; +types; +types.Value; +var v; +var a = {}; +var b = {}; +var c = ""; diff --git a/tests/baselines/reference/importClause_namespaceImport.symbols b/tests/baselines/reference/importClause_namespaceImport.symbols new file mode 100644 index 0000000000000..4242b05a4ca9d --- /dev/null +++ b/tests/baselines/reference/importClause_namespaceImport.symbols @@ -0,0 +1,42 @@ +=== /a.ts === +export class A { a!: string } +>A : Symbol(A, Decl(a.ts, 0, 0)) +>a : Symbol(A.a, Decl(a.ts, 0, 16)) + +export class B { b!: number } +>B : Symbol(B, Decl(a.ts, 0, 29)) +>b : Symbol(B.b, Decl(a.ts, 1, 16)) + +export type C = T; +>C : Symbol(C, Decl(a.ts, 1, 29)) +>T : Symbol(T, Decl(a.ts, 2, 14)) +>T : Symbol(T, Decl(a.ts, 2, 14)) + +export const Value = {}; +>Value : Symbol(Value, Decl(a.ts, 3, 12)) + +=== /b.ts === +import type * as types from './a'; +>types : Symbol(types, Decl(b.ts, 0, 11)) + +types; +types.Value; +let v: types.Value; +>v : Symbol(v, Decl(b.ts, 3, 3)) +>types : Symbol(types, Decl(b.ts, 0, 11)) + +const a: types.A = {}; +>a : Symbol(a, Decl(b.ts, 4, 5)) +>types : Symbol(types, Decl(b.ts, 0, 11)) +>A : Symbol(types.A) + +const b: types.B = {}; +>b : Symbol(b, Decl(b.ts, 5, 5)) +>types : Symbol(types, Decl(b.ts, 0, 11)) +>B : Symbol(types.B) + +const c: types.C = ""; +>c : Symbol(c, Decl(b.ts, 6, 5)) +>types : Symbol(types, Decl(b.ts, 0, 11)) +>C : Symbol(types.C, Decl(a.ts, 1, 29)) + diff --git a/tests/baselines/reference/importClause_namespaceImport.types b/tests/baselines/reference/importClause_namespaceImport.types new file mode 100644 index 0000000000000..319edbd5632c3 --- /dev/null +++ b/tests/baselines/reference/importClause_namespaceImport.types @@ -0,0 +1,47 @@ +=== /a.ts === +export class A { a!: string } +>A : A +>a : string + +export class B { b!: number } +>B : B +>b : number + +export type C = T; +>C : T + +export const Value = {}; +>Value : {} +>{} : {} + +=== /b.ts === +import type * as types from './a'; +>types : any + +types; +>types : any + +types.Value; +>types.Value : any +>types : any +>Value : any + +let v: types.Value; +>v : any +>types : any + +const a: types.A = {}; +>a : import("/a").A +>types : any +>{} : {} + +const b: types.B = {}; +>b : import("/a").B +>types : any +>{} : {} + +const c: types.C = ""; +>c : string +>types : any +>"" : "" + diff --git a/tests/baselines/reference/importDefaultNamedType.js b/tests/baselines/reference/importDefaultNamedType.js new file mode 100644 index 0000000000000..72f4f462ce4a5 --- /dev/null +++ b/tests/baselines/reference/importDefaultNamedType.js @@ -0,0 +1,21 @@ +//// [tests/cases/conformance/externalModules/typeOnly/importDefaultNamedType.ts] //// + +//// [a.ts] +export default class A {} + +//// [b.ts] +import type from './a'; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +exports["default"] = A; +//// [b.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/importDefaultNamedType.symbols b/tests/baselines/reference/importDefaultNamedType.symbols new file mode 100644 index 0000000000000..c8c17d5584615 --- /dev/null +++ b/tests/baselines/reference/importDefaultNamedType.symbols @@ -0,0 +1,8 @@ +=== /a.ts === +export default class A {} +>A : Symbol(A, Decl(a.ts, 0, 0)) + +=== /b.ts === +import type from './a'; +>type : Symbol(type, Decl(b.ts, 0, 6)) + diff --git a/tests/baselines/reference/importDefaultNamedType.types b/tests/baselines/reference/importDefaultNamedType.types new file mode 100644 index 0000000000000..496fc7fe53334 --- /dev/null +++ b/tests/baselines/reference/importDefaultNamedType.types @@ -0,0 +1,8 @@ +=== /a.ts === +export default class A {} +>A : A + +=== /b.ts === +import type from './a'; +>type : typeof type + diff --git a/tests/baselines/reference/importEqualsDeclarationError.errors.txt b/tests/baselines/reference/importEqualsDeclarationError.errors.txt new file mode 100644 index 0000000000000..1d3f606b3e048 --- /dev/null +++ b/tests/baselines/reference/importEqualsDeclarationError.errors.txt @@ -0,0 +1,17 @@ +/c.ts(1,1): error TS1370: Only ECMAScript imports may use 'import type'. + + +==== /c.ts (1 errors) ==== + import type T = require('./a'); // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1370: Only ECMAScript imports may use 'import type'. + import type = require('./b'); // Ok + +==== /a.ts (0 errors) ==== + type T = {}; + export = T; + +==== /b.ts (0 errors) ==== + class SomeClass {} + export = SomeClass; + \ No newline at end of file diff --git a/tests/baselines/reference/importsNotUsedAsValue_error.errors.txt b/tests/baselines/reference/importsNotUsedAsValue_error.errors.txt new file mode 100644 index 0000000000000..6f6d8f6f3db7e --- /dev/null +++ b/tests/baselines/reference/importsNotUsedAsValue_error.errors.txt @@ -0,0 +1,37 @@ +/b.ts(1,1): error TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValue' 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 'importsNotUsedAsValue' is set to 'error'. +/e.ts(1,1): error TS6192: All imports in import declaration are unused. + + +==== /a.ts (0 errors) ==== + export default class {} + export class A {} + export type B = {}; + +==== /b.ts (1 errors) ==== + import { A, B } from './a'; // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValue' is set to 'error'. + let a: A; + let b: B; + console.log(a, b); + +==== /c.ts (1 errors) ==== + import Default, * as named from './a'; // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValue' is set to 'error'. + let a: Default; + let b: named.B; + console.log(a, b); + +==== /d.ts (0 errors) ==== + import Default, { A } from './a'; + const a = A; + let b: Default; + console.log(a, b); + +==== /e.ts (1 errors) ==== + import { A, B } from './a'; // noUnusedLocals error only + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS6192: All imports in import declaration are unused. + \ No newline at end of file diff --git a/tests/baselines/reference/importsNotUsedAsValue_error.js b/tests/baselines/reference/importsNotUsedAsValue_error.js new file mode 100644 index 0000000000000..fe70b0dbe60a7 --- /dev/null +++ b/tests/baselines/reference/importsNotUsedAsValue_error.js @@ -0,0 +1,69 @@ +//// [tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValue_error.ts] //// + +//// [a.ts] +export default class {} +export class A {} +export type B = {}; + +//// [b.ts] +import { A, B } from './a'; // Error +let a: A; +let b: B; +console.log(a, b); + +//// [c.ts] +import Default, * as named from './a'; // Error +let a: Default; +let b: named.B; +console.log(a, b); + +//// [d.ts] +import Default, { A } from './a'; +const a = A; +let b: Default; +console.log(a, b); + +//// [e.ts] +import { A, B } from './a'; // noUnusedLocals error only + + +//// [a.js] +"use strict"; +exports.__esModule = true; +var default_1 = /** @class */ (function () { + function default_1() { + } + return default_1; +}()); +exports["default"] = default_1; +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +exports.A = A; +//// [b.js] +"use strict"; +exports.__esModule = true; +require("./a"); // Error +var a; +var b; +console.log(a, b); +//// [c.js] +"use strict"; +exports.__esModule = true; +require("./a"); // Error +var a; +var b; +console.log(a, b); +//// [d.js] +"use strict"; +exports.__esModule = true; +var a_1 = require("./a"); +var a = a_1.A; +var b; +console.log(a, b); +//// [e.js] +"use strict"; +exports.__esModule = true; +require("./a"); // noUnusedLocals error only diff --git a/tests/baselines/reference/importsNotUsedAsValue_error.symbols b/tests/baselines/reference/importsNotUsedAsValue_error.symbols new file mode 100644 index 0000000000000..0726f61086fa5 --- /dev/null +++ b/tests/baselines/reference/importsNotUsedAsValue_error.symbols @@ -0,0 +1,74 @@ +=== /a.ts === +export default class {} +export class A {} +>A : Symbol(A, Decl(a.ts, 0, 23)) + +export type B = {}; +>B : Symbol(B, Decl(a.ts, 1, 17)) + +=== /b.ts === +import { A, B } from './a'; // Error +>A : Symbol(A, Decl(b.ts, 0, 8)) +>B : Symbol(B, Decl(b.ts, 0, 11)) + +let a: A; +>a : Symbol(a, Decl(b.ts, 1, 3)) +>A : Symbol(A, Decl(b.ts, 0, 8)) + +let b: B; +>b : Symbol(b, Decl(b.ts, 2, 3)) +>B : Symbol(B, Decl(b.ts, 0, 11)) + +console.log(a, b); +>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, --, --)) +>a : Symbol(a, Decl(b.ts, 1, 3)) +>b : Symbol(b, Decl(b.ts, 2, 3)) + +=== /c.ts === +import Default, * as named from './a'; // Error +>Default : Symbol(Default, Decl(c.ts, 0, 6)) +>named : Symbol(named, Decl(c.ts, 0, 15)) + +let a: Default; +>a : Symbol(a, Decl(c.ts, 1, 3)) +>Default : Symbol(Default, Decl(c.ts, 0, 6)) + +let b: named.B; +>b : Symbol(b, Decl(c.ts, 2, 3)) +>named : Symbol(named, Decl(c.ts, 0, 15)) +>B : Symbol(named.B, Decl(a.ts, 1, 17)) + +console.log(a, b); +>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, --, --)) +>a : Symbol(a, Decl(c.ts, 1, 3)) +>b : Symbol(b, Decl(c.ts, 2, 3)) + +=== /d.ts === +import Default, { A } from './a'; +>Default : Symbol(Default, Decl(d.ts, 0, 6)) +>A : Symbol(A, Decl(d.ts, 0, 17)) + +const a = A; +>a : Symbol(a, Decl(d.ts, 1, 5)) +>A : Symbol(A, Decl(d.ts, 0, 17)) + +let b: Default; +>b : Symbol(b, Decl(d.ts, 2, 3)) +>Default : Symbol(Default, Decl(d.ts, 0, 6)) + +console.log(a, b); +>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, --, --)) +>a : Symbol(a, Decl(d.ts, 1, 5)) +>b : Symbol(b, Decl(d.ts, 2, 3)) + +=== /e.ts === +import { A, B } from './a'; // noUnusedLocals error only +>A : Symbol(A, Decl(e.ts, 0, 8)) +>B : Symbol(B, Decl(e.ts, 0, 11)) + diff --git a/tests/baselines/reference/importsNotUsedAsValue_error.types b/tests/baselines/reference/importsNotUsedAsValue_error.types new file mode 100644 index 0000000000000..5b166c47e684e --- /dev/null +++ b/tests/baselines/reference/importsNotUsedAsValue_error.types @@ -0,0 +1,72 @@ +=== /a.ts === +export default class {} +export class A {} +>A : A + +export type B = {}; +>B : B + +=== /b.ts === +import { A, B } from './a'; // Error +>A : typeof A +>B : any + +let a: A; +>a : A + +let b: B; +>b : B + +console.log(a, b); +>console.log(a, b) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>a : A +>b : B + +=== /c.ts === +import Default, * as named from './a'; // Error +>Default : typeof Default +>named : typeof named + +let a: Default; +>a : Default + +let b: named.B; +>b : named.B +>named : any + +console.log(a, b); +>console.log(a, b) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>a : Default +>b : named.B + +=== /d.ts === +import Default, { A } from './a'; +>Default : typeof Default +>A : typeof A + +const a = A; +>a : typeof A +>A : typeof A + +let b: Default; +>b : Default + +console.log(a, b); +>console.log(a, b) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>a : typeof A +>b : Default + +=== /e.ts === +import { A, B } from './a'; // noUnusedLocals error only +>A : typeof A +>B : any + diff --git a/tests/baselines/reference/isolatedModulesReExportType.errors.txt b/tests/baselines/reference/isolatedModulesReExportType.errors.txt index dc035a84c2fc9..299bd0fcf88e6 100644 --- a/tests/baselines/reference/isolatedModulesReExportType.errors.txt +++ b/tests/baselines/reference/isolatedModulesReExportType.errors.txt @@ -1,12 +1,12 @@ -/user.ts(2,10): error TS1205: Cannot re-export a type when the '--isolatedModules' flag is provided. -/user.ts(17,10): error TS1205: Cannot re-export a type when the '--isolatedModules' flag is provided. +/user.ts(2,10): error TS1205: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'. +/user.ts(17,10): error TS1205: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'. ==== /user.ts (2 errors) ==== // Error, can't re-export something that's only a type. export { T } from "./exportT"; ~ -!!! error TS1205: Cannot re-export a type when the '--isolatedModules' flag is provided. +!!! error TS1205: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'. export import T2 = require("./exportEqualsT"); // OK, has a value side @@ -23,7 +23,7 @@ import { T } from "./exportT"; export { T as T4 }; ~~~~~~~ -!!! error TS1205: Cannot re-export a type when the '--isolatedModules' flag is provided. +!!! error TS1205: Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'. ==== /exportT.ts (0 errors) ==== export type T = number; diff --git a/tests/baselines/reference/preserveUnusedImports.js b/tests/baselines/reference/preserveUnusedImports.js new file mode 100644 index 0000000000000..7c2c089a72729 --- /dev/null +++ b/tests/baselines/reference/preserveUnusedImports.js @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/preserveUnusedImports.ts] //// + +//// [a.ts] +export type A = {}; + +//// [b.ts] +export class B {} + +//// [c.ts] +import { A } from './a'; +import { B } from './b'; + +let b: B; + + +//// [a.js] +"use strict"; +exports.__esModule = true; +//// [b.js] +"use strict"; +exports.__esModule = true; +var B = /** @class */ (function () { + function B() { + } + return B; +}()); +exports.B = B; +//// [c.js] +"use strict"; +exports.__esModule = true; +require("./a"); +require("./b"); +var b; diff --git a/tests/baselines/reference/preserveUnusedImports.symbols b/tests/baselines/reference/preserveUnusedImports.symbols new file mode 100644 index 0000000000000..1b7189408eedd --- /dev/null +++ b/tests/baselines/reference/preserveUnusedImports.symbols @@ -0,0 +1,19 @@ +=== tests/cases/compiler/a.ts === +export type A = {}; +>A : Symbol(A, Decl(a.ts, 0, 0)) + +=== tests/cases/compiler/b.ts === +export class B {} +>B : Symbol(B, Decl(b.ts, 0, 0)) + +=== tests/cases/compiler/c.ts === +import { A } from './a'; +>A : Symbol(A, Decl(c.ts, 0, 8)) + +import { B } from './b'; +>B : Symbol(B, Decl(c.ts, 1, 8)) + +let b: B; +>b : Symbol(b, Decl(c.ts, 3, 3)) +>B : Symbol(B, Decl(c.ts, 1, 8)) + diff --git a/tests/baselines/reference/preserveUnusedImports.types b/tests/baselines/reference/preserveUnusedImports.types new file mode 100644 index 0000000000000..e4e32ebb27ebb --- /dev/null +++ b/tests/baselines/reference/preserveUnusedImports.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/a.ts === +export type A = {}; +>A : A + +=== tests/cases/compiler/b.ts === +export class B {} +>B : B + +=== tests/cases/compiler/c.ts === +import { A } from './a'; +>A : any + +import { B } from './b'; +>B : typeof B + +let b: B; +>b : B + diff --git a/tests/baselines/reference/renamed.errors.txt b/tests/baselines/reference/renamed.errors.txt new file mode 100644 index 0000000000000..8f75a47757714 --- /dev/null +++ b/tests/baselines/reference/renamed.errors.txt @@ -0,0 +1,17 @@ +/c.ts(2,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 B }; + +==== /b.ts (0 errors) ==== + export type { B as C } from './a'; + +==== /c.ts (1 errors) ==== + import type { C as D } from './b'; + const d: D = {}; + ~ +!!! 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/renamed.js b/tests/baselines/reference/renamed.js new file mode 100644 index 0000000000000..b4e539bacb721 --- /dev/null +++ b/tests/baselines/reference/renamed.js @@ -0,0 +1,29 @@ +//// [tests/cases/conformance/externalModules/typeOnly/renamed.ts] //// + +//// [a.ts] +class A { a!: string } +export type { A as B }; + +//// [b.ts] +export type { B as C } from './a'; + +//// [c.ts] +import type { C as D } from './b'; +const d: D = {}; + + +//// [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 d = {}; diff --git a/tests/baselines/reference/renamed.symbols b/tests/baselines/reference/renamed.symbols new file mode 100644 index 0000000000000..8af253a8c0370 --- /dev/null +++ b/tests/baselines/reference/renamed.symbols @@ -0,0 +1,23 @@ +=== /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 B }; +>A : Symbol(A) +>B : Symbol(B, Decl(a.ts, 1, 13)) + +=== /b.ts === +export type { B as C } from './a'; +>B : Symbol(B, Decl(a.ts, 1, 13)) +>C : Symbol(C, Decl(b.ts, 0, 13)) + +=== /c.ts === +import type { C as D } from './b'; +>C : Symbol(C, Decl(b.ts, 0, 13)) +>D : Symbol(D, Decl(c.ts, 0, 13)) + +const d: D = {}; +>d : Symbol(d, Decl(c.ts, 1, 5)) +>D : Symbol(D, Decl(c.ts, 0, 13)) + diff --git a/tests/baselines/reference/renamed.types b/tests/baselines/reference/renamed.types new file mode 100644 index 0000000000000..85a9bfcc4002c --- /dev/null +++ b/tests/baselines/reference/renamed.types @@ -0,0 +1,23 @@ +=== /a.ts === +class A { a!: string } +>A : A +>a : string + +export type { A as B }; +>A : A +>B : A + +=== /b.ts === +export type { B as C } from './a'; +>B : A +>C : A + +=== /c.ts === +import type { C as D } from './b'; +>C : A +>D : A + +const d: D = {}; +>d : A +>{} : {} + diff --git a/tests/baselines/reference/showConfig/Shows tsconfig for single option/importsNotUsedAsValue/tsconfig.json b/tests/baselines/reference/showConfig/Shows tsconfig for single option/importsNotUsedAsValue/tsconfig.json new file mode 100644 index 0000000000000..f94214e3e9e58 --- /dev/null +++ b/tests/baselines/reference/showConfig/Shows tsconfig for single option/importsNotUsedAsValue/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "importsNotUsedAsValue": "remove" + } +} diff --git a/tests/baselines/reference/topLevelAwait(module=esnext,target=es2015).errors.txt b/tests/baselines/reference/topLevelAwait(module=esnext,target=es2015).errors.txt index 9efe0c2aa612f..0bd384b85206d 100644 --- a/tests/baselines/reference/topLevelAwait(module=esnext,target=es2015).errors.txt +++ b/tests/baselines/reference/topLevelAwait(module=esnext,target=es2015).errors.txt @@ -1,9 +1,9 @@ -tests/cases/conformance/externalModules/topLevelAwait.ts(2,1): error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. +tests/cases/conformance/externalModules/topLevelAwait.ts(2,1): error TS1375: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. ==== tests/cases/conformance/externalModules/topLevelAwait.ts (1 errors) ==== export const x = 1; await x; ~~~~~ -!!! error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. +!!! error TS1375: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. \ No newline at end of file diff --git a/tests/baselines/reference/topLevelAwait(module=system,target=es2015).errors.txt b/tests/baselines/reference/topLevelAwait(module=system,target=es2015).errors.txt index 9efe0c2aa612f..0bd384b85206d 100644 --- a/tests/baselines/reference/topLevelAwait(module=system,target=es2015).errors.txt +++ b/tests/baselines/reference/topLevelAwait(module=system,target=es2015).errors.txt @@ -1,9 +1,9 @@ -tests/cases/conformance/externalModules/topLevelAwait.ts(2,1): error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. +tests/cases/conformance/externalModules/topLevelAwait.ts(2,1): error TS1375: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. ==== tests/cases/conformance/externalModules/topLevelAwait.ts (1 errors) ==== export const x = 1; await x; ~~~~~ -!!! error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. +!!! error TS1375: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. \ No newline at end of file diff --git a/tests/baselines/reference/topLevelAwaitNonModule.errors.txt b/tests/baselines/reference/topLevelAwaitNonModule.errors.txt index 8e2d3a6eab3cd..888c565e7a9d5 100644 --- a/tests/baselines/reference/topLevelAwaitNonModule.errors.txt +++ b/tests/baselines/reference/topLevelAwaitNonModule.errors.txt @@ -1,11 +1,11 @@ -tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts(1,1): error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. +tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts(1,1): error TS1375: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts(1,7): error TS2304: Cannot find name 'x'. ==== tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts (2 errors) ==== await x; ~~~~~ -!!! error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. +!!! error TS1375: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher. ~ !!! error TS2304: Cannot find name 'x'. \ No newline at end of file diff --git a/tests/cases/compiler/preserveUnusedImports.ts b/tests/cases/compiler/preserveUnusedImports.ts new file mode 100644 index 0000000000000..e6d4497ef1592 --- /dev/null +++ b/tests/cases/compiler/preserveUnusedImports.ts @@ -0,0 +1,13 @@ +// @importsNotUsedAsValue: preserve + +// @Filename: a.ts +export type A = {}; + +// @Filename: b.ts +export class B {} + +// @Filename: c.ts +import { A } from './a'; +import { B } from './b'; + +let b: B; diff --git a/tests/cases/conformance/externalModules/typeOnly/chained.ts b/tests/cases/conformance/externalModules/typeOnly/chained.ts new file mode 100644 index 0000000000000..57a0faad738aa --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/chained.ts @@ -0,0 +1,17 @@ +// @Filename: /a.ts +class A { a!: string } +export type { A as B }; +export type Z = A; + +// @Filename: /b.ts +import { Z as Y } from './a'; +export { B as C } from './a'; + +// @Filename: /c.ts +import type { C } from './b'; +export { C as D }; + +// @Filename: /d.ts +import { D } from './c'; +new D(); +const d: D = {}; diff --git a/tests/cases/conformance/externalModules/typeOnly/circular1.ts b/tests/cases/conformance/externalModules/typeOnly/circular1.ts new file mode 100644 index 0000000000000..51fe382040e3e --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/circular1.ts @@ -0,0 +1,7 @@ +// @noEmit: true + +// @Filename: /a.ts +export type { A } from './b'; + +// @Filename: /b.ts +export type { A } from './a'; diff --git a/tests/cases/conformance/externalModules/typeOnly/circular2.ts b/tests/cases/conformance/externalModules/typeOnly/circular2.ts new file mode 100644 index 0000000000000..a11f0a48fe0a7 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/circular2.ts @@ -0,0 +1,9 @@ +// @noEmit: true + +// @Filename: /a.ts +import type { B } from './b'; +export type A = B; + +// @Filename: /b.ts +import type { A } from './a'; +export type B = A; diff --git a/tests/cases/conformance/externalModules/typeOnly/circular3.ts b/tests/cases/conformance/externalModules/typeOnly/circular3.ts new file mode 100644 index 0000000000000..0cd40f250098c --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/circular3.ts @@ -0,0 +1,9 @@ +// @noEmit: true + +// @Filename: /a.ts +import type { A } from './b'; +export type { A as B }; + +// @Filename: /b.ts +import type { B } from './a'; +export type { B as A }; diff --git a/tests/cases/conformance/externalModules/typeOnly/circular4.ts b/tests/cases/conformance/externalModules/typeOnly/circular4.ts new file mode 100644 index 0000000000000..5dab72f03cf69 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/circular4.ts @@ -0,0 +1,17 @@ +// @noEmit: true + +// @Filename: /a.ts +import type { ns2 } from './b'; +export namespace ns1 { + export namespace nested { + export type T = ns2.nested.T; + } +} + +// @Filename: /b.ts +import type { ns1 } from './a'; +export namespace ns2 { + export namespace nested { + export type T = ns1.nested.T; + } +} diff --git a/tests/cases/conformance/externalModules/typeOnly/enums.ts b/tests/cases/conformance/externalModules/typeOnly/enums.ts new file mode 100644 index 0000000000000..b632bdbce546b --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/enums.ts @@ -0,0 +1,29 @@ +// @Filename: /a.ts +enum SyntaxKind { + ImportClause, + ExportDeclaration +} + +const enum SymbolFlags { + Type = "Type", + Value = "Value" +} + +export type { SyntaxKind }; +export { SymbolFlags }; + +// @Filename: /b.ts +import type { SyntaxKind, SymbolFlags } from './a'; + +SyntaxKind.ImportClause; +SymbolFlags.Type; +let kind: SyntaxKind.ImportClause; +let flags: SymbolFlags; + +type TypeFlag = SymbolFlags.Type; +export type { TypeFlag }; + +// @Filename: /c.ts +import { SymbolFlags } from './a'; +import type { TypeFlag } from './b'; +const flags: TypeFlag = SymbolFlags.Type; diff --git a/tests/cases/conformance/externalModules/typeOnly/exportDeclaration.ts b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration.ts new file mode 100644 index 0000000000000..d492ece20901d --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration.ts @@ -0,0 +1,8 @@ +// @Filename: /a.ts +class A {} +export type { A }; + +// @Filename: /b.ts +import { A } from './a'; +declare const a: A; +new A(); diff --git a/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts new file mode 100644 index 0000000000000..e46cd4a69812f --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_missingBraces.ts @@ -0,0 +1,20 @@ +// @noEmit: true +// @noTypesAndSymbols: true + +enum Numbers { + One, + Two +} + +class Box {} + +interface Circle {} + +namespace ns { + export type T; // Normal parse error because there is no other 'T' +} + +export type Numbers; +export type Box; +export type Circle; +export type ns; diff --git a/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_moduleSpecifier-isolatedModules.ts b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_moduleSpecifier-isolatedModules.ts new file mode 100644 index 0000000000000..e625e9065a0fc --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_moduleSpecifier-isolatedModules.ts @@ -0,0 +1,7 @@ +// @isolatedModules: true + +// @Filename: /a.ts +export type A = {}; + +// @Filename: /b.ts +export type { A } from './a'; // should not error, but would without `type` diff --git a/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_moduleSpecifier.ts b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_moduleSpecifier.ts new file mode 100644 index 0000000000000..d33b6fe990047 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_moduleSpecifier.ts @@ -0,0 +1,10 @@ +// @Filename: /a.ts +export class A {} + +// @Filename: /b.ts +export type { A } from './a'; + +// @Filename: /c.ts +import { A } from './b'; +declare const a: A; +new A(); diff --git a/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_value.ts b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_value.ts new file mode 100644 index 0000000000000..1ae4c63b31049 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/exportDeclaration_value.ts @@ -0,0 +1,7 @@ +// @Filename: /a.ts +const A = {}; +export type { A }; +export const AA = {}; + +// @Filename: /b.ts +export type { AA } from './a'; diff --git a/tests/cases/conformance/externalModules/typeOnly/filterNamespace_import.ts b/tests/cases/conformance/externalModules/typeOnly/filterNamespace_import.ts new file mode 100644 index 0000000000000..b0cd26e0a8a6c --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/filterNamespace_import.ts @@ -0,0 +1,21 @@ +// @Filename: /ns.ts +namespace ns { + export type Type = string; + export class Class {} + export const Value = ""; + export namespace nested { + export class NestedClass { + a!: string; + } + } +} + +export default ns; + +// @Filename: /a.ts +import type ns from './ns'; +ns.Class; // Error +ns.Value; // Error +let c: ns.Class; +let t: ns.Type = ""; +let n: ns.nested.NestedClass = { a: '' }; diff --git a/tests/cases/conformance/externalModules/typeOnly/generic.ts b/tests/cases/conformance/externalModules/typeOnly/generic.ts new file mode 100644 index 0000000000000..5ea17492f1b8f --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/generic.ts @@ -0,0 +1,11 @@ +// @Filename: /a.ts +export class A { a!: T } +export type { A as B }; + +// @Filename: /b.ts +import type { A } from './a'; +import { B } from './a'; +let a: A = { a: "" }; +let b: B = { a: 3 }; +let c: A = {}; +let d: B = { a: "" }; \ No newline at end of file diff --git a/tests/cases/conformance/externalModules/typeOnly/grammarErrors.ts b/tests/cases/conformance/externalModules/typeOnly/grammarErrors.ts new file mode 100644 index 0000000000000..e16efa45f9115 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/grammarErrors.ts @@ -0,0 +1,15 @@ +// @noTypesAndSymbols: true +// @allowJs: true +// @checkJs: true + +// @Filename: /a.ts +export default class A {} +export class B {} +export class C {} + +// @Filename: /b.ts +import type A, { B, C } from './a'; + +// @Filename: /a.js +import type A from './a'; +export type { A }; diff --git a/tests/cases/conformance/externalModules/typeOnly/importClause_default.ts b/tests/cases/conformance/externalModules/typeOnly/importClause_default.ts new file mode 100644 index 0000000000000..b3f5a5259e4cf --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/importClause_default.ts @@ -0,0 +1,7 @@ +// @Filename: /a.ts +export default class A { a!: string } + +// @Filename: /b.ts +import type A from './a'; +new A(); +let a: A = { a: '' }; diff --git a/tests/cases/conformance/externalModules/typeOnly/importClause_namedImports.ts b/tests/cases/conformance/externalModules/typeOnly/importClause_namedImports.ts new file mode 100644 index 0000000000000..c2642701a35ec --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/importClause_namedImports.ts @@ -0,0 +1,11 @@ +// @Filename: /abc.ts +export class A {} +export type B = { b: string }; +export const C = ""; + +// @Filename: /d.ts +import type { A, B, C } from './abc'; +new A(); +declare let a: A; +declare let b: B; +b.b; diff --git a/tests/cases/conformance/externalModules/typeOnly/importClause_namespaceImport.ts b/tests/cases/conformance/externalModules/typeOnly/importClause_namespaceImport.ts new file mode 100644 index 0000000000000..d9a6313c4d1c2 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/importClause_namespaceImport.ts @@ -0,0 +1,14 @@ +// @Filename: /a.ts +export class A { a!: string } +export class B { b!: number } +export type C = T; +export const Value = {}; + +// @Filename: /b.ts +import type * as types from './a'; +types; +types.Value; +let v: types.Value; +const a: types.A = {}; +const b: types.B = {}; +const c: types.C = ""; diff --git a/tests/cases/conformance/externalModules/typeOnly/importDefaultNamedType.ts b/tests/cases/conformance/externalModules/typeOnly/importDefaultNamedType.ts new file mode 100644 index 0000000000000..507ad447710d4 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/importDefaultNamedType.ts @@ -0,0 +1,5 @@ +// @Filename: /a.ts +export default class A {} + +// @Filename: /b.ts +import type from './a'; diff --git a/tests/cases/conformance/externalModules/typeOnly/importEqualsDeclarationError.ts b/tests/cases/conformance/externalModules/typeOnly/importEqualsDeclarationError.ts new file mode 100644 index 0000000000000..f3b40cd48dca0 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/importEqualsDeclarationError.ts @@ -0,0 +1,14 @@ +// @noEmit: true +// @noTypesAndSymbols: true + +// @Filename: /a.ts +type T = {}; +export = T; + +// @Filename: /b.ts +class SomeClass {} +export = SomeClass; + +// @Filename: /c.ts +import type T = require('./a'); // Error +import type = require('./b'); // Ok diff --git a/tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValue_error.ts b/tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValue_error.ts new file mode 100644 index 0000000000000..c74dfe8195167 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/importsNotUsedAsValue_error.ts @@ -0,0 +1,28 @@ +// @importsNotUsedAsValue: error +// @noUnusedLocals: true + +// @Filename: /a.ts +export default class {} +export class A {} +export type B = {}; + +// @Filename: /b.ts +import { A, B } from './a'; // Error +let a: A; +let b: B; +console.log(a, b); + +// @Filename: /c.ts +import Default, * as named from './a'; // Error +let a: Default; +let b: named.B; +console.log(a, b); + +// @Filename: /d.ts +import Default, { A } from './a'; +const a = A; +let b: Default; +console.log(a, b); + +// @Filename: /e.ts +import { A, B } from './a'; // noUnusedLocals error only diff --git a/tests/cases/conformance/externalModules/typeOnly/renamed.ts b/tests/cases/conformance/externalModules/typeOnly/renamed.ts new file mode 100644 index 0000000000000..266407b69e613 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/renamed.ts @@ -0,0 +1,11 @@ +// @strict: true +// @Filename: /a.ts +class A { a!: string } +export type { A as B }; + +// @Filename: /b.ts +export type { B as C } from './a'; + +// @Filename: /c.ts +import type { C as D } from './b'; +const d: D = {}; diff --git a/tests/cases/fourslash/codeFixConvertToTypeOnlyExport1.ts b/tests/cases/fourslash/codeFixConvertToTypeOnlyExport1.ts new file mode 100644 index 0000000000000..00c9083f37720 --- /dev/null +++ b/tests/cases/fourslash/codeFixConvertToTypeOnlyExport1.ts @@ -0,0 +1,18 @@ +/// + +// @isolatedModules: true + +// @Filename: /a.ts +////export type A = {}; +////export type B = {}; + +// @Filename: /b.ts +////export { A, B } from './a'; + +goTo.file("/b.ts"); +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Convert_to_type_only_export.message, + errorCode: ts.Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type.code, + newFileContent: "export type { A, B } from './a';" +}); diff --git a/tests/cases/fourslash/codeFixConvertToTypeOnlyExport2.ts b/tests/cases/fourslash/codeFixConvertToTypeOnlyExport2.ts new file mode 100644 index 0000000000000..16733b082ab04 --- /dev/null +++ b/tests/cases/fourslash/codeFixConvertToTypeOnlyExport2.ts @@ -0,0 +1,22 @@ +/// + +// @isolatedModules: true + +// @Filename: /a.ts +////export type A = {}; +////export const B = {}; +////export type C = {}; + +// @Filename: /b.ts +////export { A, B, C } from './a'; + +goTo.file("/b.ts"); +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Convert_to_type_only_export.message, + errorCode: ts.Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type.code, + newFileContent: +`export { B } from './a'; +export type { A, C } from './a'; +` +}); diff --git a/tests/cases/fourslash/codeFixConvertToTypeOnlyExport3.ts b/tests/cases/fourslash/codeFixConvertToTypeOnlyExport3.ts new file mode 100644 index 0000000000000..f90e0657d511d --- /dev/null +++ b/tests/cases/fourslash/codeFixConvertToTypeOnlyExport3.ts @@ -0,0 +1,29 @@ +/// + +// @isolatedModules: true + +// @Filename: /a.ts +////export type T1 = {}; +////export const V1 = {}; +////export type T2 = {}; + +// @Filename: /b.ts +////export type T3 = {}; +////export const V2 = {}; +////export type T4 = {}; + +// @Filename: /c.ts +////export { T1, V1, T2 } from './a'; +////export { T3, V2, T4 } from './b'; + +goTo.file("/c.ts"); +verify.codeFixAll({ + fixAllDescription: ts.Diagnostics.Convert_all_re_exported_types_to_type_only_exports.message, + fixId: "convertToTypeOnlyExport", + newFileContent: +`export { V1 } from './a'; +export type { T1, T2 } from './a'; +export { V2 } from './b'; +export type { T3, T4 } from './b'; +` +}); diff --git a/tests/cases/fourslash/codeFixInferFromUsageContextualImport2.ts b/tests/cases/fourslash/codeFixInferFromUsageContextualImport2.ts index f12f9dc9e80ed..8b88015bcfd50 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageContextualImport2.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageContextualImport2.ts @@ -20,7 +20,7 @@ goTo.file("/b.ts"); verify.codeFix({ description: "Infer parameter types from usage", newFileContent: -`import { User } from "./a"; +`import type { User } from "./a"; export function f(user: User) { getEmail(user); diff --git a/tests/cases/fourslash/codeFixSplitTypeOnlyImport.ts b/tests/cases/fourslash/codeFixSplitTypeOnlyImport.ts new file mode 100644 index 0000000000000..00607ac2fc071 --- /dev/null +++ b/tests/cases/fourslash/codeFixSplitTypeOnlyImport.ts @@ -0,0 +1,39 @@ +/// + +// @Filename: /a.ts +////export type B = {}; +////export type C = {}; +////export default interface A {} + +// @Filename: /b.ts +////export type E = {}; +////export type F = {}; +////export default class D {} + +// @Filename: /c.ts +////import type A, { B, C } from './a'; +////import type D, * as types from './b'; + +goTo.file("/c.ts"); + +verify.codeFix({ + errorCode: ts.Diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both.code, + description: ts.Diagnostics.Split_into_two_separate_import_declarations.message, + applyChanges: false, + index: 0, + newFileContent: +`import type A from './a'; +import type { B, C } from './a'; +import type D, * as types from './b';` +}); + +verify.codeFixAll({ + fixId: "splitTypeOnlyImport", + fixAllDescription: ts.Diagnostics.Split_all_invalid_type_only_imports.message, + newFileContent: +`import type A from './a'; +import type { B, C } from './a'; +import type D from './b'; +import type * as types from './b'; +` +}); diff --git a/tests/cases/fourslash/completionsImport_exportEquals.ts b/tests/cases/fourslash/completionsImport_exportEquals.ts index 1ea3e7e637ea8..4a5d5fdd16d4d 100644 --- a/tests/cases/fourslash/completionsImport_exportEquals.ts +++ b/tests/cases/fourslash/completionsImport_exportEquals.ts @@ -17,7 +17,7 @@ const preferences: FourSlashInterface.UserPreferences = { includeCompletionsForM verify.completions( { marker: "0", - includes: { + includes: { name: "a", source: "/a", hasAction: true, @@ -28,10 +28,10 @@ verify.completions( { marker: "1", includes: { - name: "b", - source: "/a", - hasAction: true, - sortText: completion.SortText.AutoImportSuggestions + name: "b", + source: "/a", + hasAction: true, + sortText: completion.SortText.AutoImportSuggestions }, preferences, } @@ -43,7 +43,7 @@ verify.applyCodeActionFromCompletion("1", { source: "/a", description: `Import 'b' from module "./a"`, newFileContent: -`import { b } from "./a"; +`import type { b } from "./a"; a; let x: b;`, @@ -54,7 +54,7 @@ verify.applyCodeActionFromCompletion("0", { source: "/a", description: `Import 'a' from module "./a"`, newFileContent: -`import { b } from "./a"; +`import type { b } from "./a"; import a = require("./a"); a; diff --git a/tests/cases/fourslash/completionsTypeOnlyNamespace.ts b/tests/cases/fourslash/completionsTypeOnlyNamespace.ts new file mode 100644 index 0000000000000..429353886f016 --- /dev/null +++ b/tests/cases/fourslash/completionsTypeOnlyNamespace.ts @@ -0,0 +1,23 @@ +/// + +// @Filename: /a.ts +////export namespace ns { +//// export class Box {} +//// export type Type = {}; +//// export const Value = {}; +////} + +// @Filename: /b.ts +////import type { ns } from './a'; +////let x: ns./**/ + +verify.completions({ + marker: "", + exact: [{ + name: "Box", + text: "class ns.Box", + }, { + name: "Type", + text: "type ns.Type = {}" + }] +}); diff --git a/tests/cases/fourslash/goToDefinitionTypeOnlyImport.ts b/tests/cases/fourslash/goToDefinitionTypeOnlyImport.ts new file mode 100644 index 0000000000000..f7a9da33d9b67 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionTypeOnlyImport.ts @@ -0,0 +1,15 @@ +/// + +// @Filename: /a.ts +////enum /*1*/SyntaxKind { SourceFile } +////export type { SyntaxKind } + +// @Filename: /b.ts +//// export type { SyntaxKind } from './a'; + +// @Filename: /c.ts +////import type { SyntaxKind } from './b'; +////let kind: [|/*2*/SyntaxKind|]; + + +verify.goToDefinition("2", "1"); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFile3.ts b/tests/cases/fourslash/importNameCodeFixNewImportFile3.ts index af7901e03e61c..30467e7654965 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportFile3.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportFile3.ts @@ -9,7 +9,7 @@ //// } verify.importFixAtPosition([ -`import { XXX } from "./module"; +`import type { XXX } from "./module"; let t: XXX.I;` ]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportFile4.ts b/tests/cases/fourslash/importNameCodeFixNewImportFile4.ts index 62cf48c4977b9..4d59eb28e5216 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportFile4.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportFile4.ts @@ -10,7 +10,7 @@ //// } verify.importFixAtPosition([ -`import { A } from "./module"; +`import type { A } from "./module"; let t: A.B.I;` ]); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixNewImportTypeOnly.ts b/tests/cases/fourslash/importNameCodeFixNewImportTypeOnly.ts new file mode 100644 index 0000000000000..3b2b61bf7a597 --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixNewImportTypeOnly.ts @@ -0,0 +1,14 @@ +/// + +// @target: esnext + +// @Filename: /a.ts +////export interface Car {} + +// @Filename: /b.ts +////function drive(car: /**/Car) {} + +goTo.file("/b.ts"); +verify.importFixAtPosition([`import type { Car } from "./a"; + +function drive(car: Car) {}`]); diff --git a/tests/cases/fourslash/importNameCodeFix_exportEquals.ts b/tests/cases/fourslash/importNameCodeFix_exportEquals.ts index 4ab87df7e7618..5dd9b9f100e7c 100644 --- a/tests/cases/fourslash/importNameCodeFix_exportEquals.ts +++ b/tests/cases/fourslash/importNameCodeFix_exportEquals.ts @@ -16,9 +16,9 @@ verify.codeFixAll({ fixId: "fixMissingImport", fixAllDescription: "Add all missing imports", newFileContent: -`import { b } from "./a"; +`import a = require("./a"); -import a = require("./a"); +import type { b } from "./a"; a; let x: b;`, diff --git a/tests/cases/fourslash/quickInfoTypeOnlyEnum.ts b/tests/cases/fourslash/quickInfoTypeOnlyEnum.ts new file mode 100644 index 0000000000000..a7ca81b01eb5c --- /dev/null +++ b/tests/cases/fourslash/quickInfoTypeOnlyEnum.ts @@ -0,0 +1,18 @@ +/// + +// @Filename: /a.ts +////export enum SyntaxKind { +//// SourceFile +////} + +// @Filename: /b.ts +////import type { SyntaxKind } from './a'; +////let x: SyntaxKind/*1*/; +////let y: SyntaxKind./*2*/SourceFile; + +verify.quickInfoAt("1", [ + "(alias) enum SyntaxKind", + "import SyntaxKind" +].join("\n")); + +verify.quickInfoAt("2", "(enum member) SyntaxKind.SourceFile = 0"); \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoTypeOnlyNamespaceAndClass.ts b/tests/cases/fourslash/quickInfoTypeOnlyNamespaceAndClass.ts new file mode 100644 index 0000000000000..900306fcc24d2 --- /dev/null +++ b/tests/cases/fourslash/quickInfoTypeOnlyNamespaceAndClass.ts @@ -0,0 +1,13 @@ +/// + +// @Filename: /a.ts +////export namespace ns { +//// export class Box {} +////} + +// @Filename: /b.ts +////import type { ns } from './a'; +////let x: /*1*/ns./*2*/Box; + +verify.quickInfoAt("1", "(alias) namespace ns\nimport ns"); +verify.quickInfoAt("2", "class ns.Box");