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"); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy