Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2478,7 +2478,7 @@ namespace ts {
const immediate = resolveExternalModuleName(
node,
getExternalModuleRequireArgument(node) || getExternalModuleImportEqualsDeclarationExpression(node));
const resolved = resolveExternalModuleSymbol(immediate);
const resolved = resolveExternalModuleSymbol(immediate);
markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, /*overwriteEmpty*/ false);
return resolved;
}
Expand All @@ -2488,7 +2488,7 @@ namespace ts {
}

function checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node: ImportEqualsDeclaration, resolved: Symbol | undefined) {
if (markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false)) {
if (markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false) && !node.isTypeOnly) {
Comment thread
andrewbranch marked this conversation as resolved.
const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(getSymbolOfNode(node))!;
const isExport = typeOnlyDeclarationIsExport(typeOnlyDeclaration);
const message = isExport
Expand Down Expand Up @@ -6837,13 +6837,15 @@ namespace ts {
addResult(factory.createImportEqualsDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*isTypeOnly*/ false,
uniqueName,
factory.createExternalModuleReference(factory.createStringLiteral(specifier))
), ModifierFlags.None);
// import x = _x.z
addResult(factory.createImportEqualsDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*isTypeOnly*/ false,
factory.createIdentifier(localName),
factory.createQualifiedName(uniqueName, initializer.name as Identifier),
), modifierFlags);
Expand All @@ -6863,6 +6865,7 @@ namespace ts {
addResult(factory.createImportEqualsDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*isTypeOnly*/ false,
factory.createIdentifier(localName),
isLocalImport
? symbolToName(target, context, SymbolFlags.All, /*expectsIdentifier*/ false)
Expand Down Expand Up @@ -7020,6 +7023,7 @@ namespace ts {
addResult(factory.createImportEqualsDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*isTypeOnly*/ false,
factory.createIdentifier(varName),
symbolToName(target, context, SymbolFlags.All, /*expectsIdentifier*/ false)
), ModifierFlags.None);
Expand Down Expand Up @@ -36421,9 +36425,12 @@ namespace ts {
checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0);
}
}
if (node.isTypeOnly) {
grammarErrorOnNode(node, Diagnostics.An_import_alias_cannot_use_import_type);
}
}
else {
if (moduleKind >= ModuleKind.ES2015 && !(node.flags & NodeFlags.Ambient)) {
if (moduleKind >= ModuleKind.ES2015 && !node.isTypeOnly && !(node.flags & NodeFlags.Ambient)) {
// Import equals declaration is deprecated in es6 or above
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
}
Expand Down Expand Up @@ -36514,19 +36521,30 @@ namespace ts {
});
}

function canConvertImportDeclarationToTypeOnly(statement: Statement) {
return isImportDeclaration(statement) &&
statement.importClause &&
!statement.importClause.isTypeOnly &&
importClauseContainsReferencedImport(statement.importClause) &&
!isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) &&
!importClauseContainsConstEnumUsedAsValue(statement.importClause);
}

function canConvertImportEqualsDeclarationToTypeOnly(statement: Statement) {
return isImportEqualsDeclaration(statement) &&
isExternalModuleReference(statement.moduleReference) &&
!statement.isTypeOnly &&
getSymbolOfNode(statement).isReferenced &&
!isReferencedAliasDeclaration(statement, /*checkChildren*/ false) &&
!getSymbolLinks(getSymbolOfNode(statement)).constEnumReferenced;
}

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) &&
!importClauseContainsConstEnumUsedAsValue(statement.importClause)
) {
if (canConvertImportDeclarationToTypeOnly(statement) || canConvertImportEqualsDeclarationToTypeOnly(statement)) {
error(
statement,
Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValues_is_set_to_error);
Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_importsNotUsedAsValues_is_set_to_error);
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1112,11 +1112,7 @@
"category": "Message",
"code": 1369
},
"Only ECMAScript imports may use 'import type'.": {
"category": "Error",
"code": 1370
Comment thread
andrewbranch marked this conversation as resolved.
},
"This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'.": {
"This import is never used as a value and must use 'import type' because 'importsNotUsedAsValues' is set to 'error'.": {
"category": "Error",
"code": 1371
},
Expand Down Expand Up @@ -1196,6 +1192,10 @@
"category": "Error",
"code": 1391
},
"An import alias cannot use 'import type'": {
"category": "Error",
"code": 1392
},
"The types of '{0}' are incompatible between these types.": {
"category": "Error",
"code": 2200
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3155,6 +3155,10 @@ namespace ts {
emitModifiers(node, node.modifiers);
emitTokenWithComment(SyntaxKind.ImportKeyword, node.modifiers ? node.modifiers.end : node.pos, writeKeyword, node);
writeSpace();
if (node.isTypeOnly) {
emitTokenWithComment(SyntaxKind.TypeKeyword, node.pos, writeKeyword, node);
writeSpace();
}
emit(node.name);
writeSpace();
emitTokenWithComment(SyntaxKind.EqualsToken, node.name.end, writePunctuation, node);
Expand Down
8 changes: 6 additions & 2 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3776,6 +3776,7 @@ namespace ts {
function createImportEqualsDeclaration(
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
isTypeOnly: boolean,
name: string | Identifier,
moduleReference: ModuleReference
) {
Expand All @@ -3785,6 +3786,7 @@ namespace ts {
modifiers,
name
);
node.isTypeOnly = isTypeOnly;
node.moduleReference = moduleReference;
node.transformFlags |= propagateChildFlags(node.moduleReference);
if (!isExternalModuleReference(node.moduleReference)) node.transformFlags |= TransformFlags.ContainsTypeScript;
Expand All @@ -3797,14 +3799,16 @@ namespace ts {
node: ImportEqualsDeclaration,
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
isTypeOnly: boolean,
name: Identifier,
moduleReference: ModuleReference
) {
return node.decorators !== decorators
|| node.modifiers !== modifiers
|| node.isTypeOnly !== isTypeOnly
|| node.name !== name
|| node.moduleReference !== moduleReference
? update(createImportEqualsDeclaration(decorators, modifiers, name, moduleReference), node)
? update(createImportEqualsDeclaration(decorators, modifiers, isTypeOnly, name, moduleReference), node)
: node;
}

Expand Down Expand Up @@ -5798,7 +5802,7 @@ namespace ts {
isTypeAliasDeclaration(node) ? updateTypeAliasDeclaration(node, node.decorators, modifiers, node.name, node.typeParameters, node.type) :
isEnumDeclaration(node) ? updateEnumDeclaration(node, node.decorators, modifiers, node.name, node.members) :
isModuleDeclaration(node) ? updateModuleDeclaration(node, node.decorators, modifiers, node.name, node.body) :
isImportEqualsDeclaration(node) ? updateImportEqualsDeclaration(node, node.decorators, modifiers, node.name, node.moduleReference) :
isImportEqualsDeclaration(node) ? updateImportEqualsDeclaration(node, node.decorators, modifiers, node.isTypeOnly, node.name, node.moduleReference) :
isImportDeclaration(node) ? updateImportDeclaration(node, node.decorators, modifiers, node.importClause, node.moduleSpecifier) :
isExportAssignment(node) ? updateExportAssignment(node, node.decorators, modifiers, node.expression) :
isExportDeclaration(node) ? updateExportDeclaration(node, node.decorators, modifiers, node.isTypeOnly, node.exportClause, node.moduleSpecifier) :
Expand Down
5 changes: 1 addition & 4 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6923,11 +6923,8 @@ namespace ts {
parseExpected(SyntaxKind.EqualsToken);
const moduleReference = parseModuleReference();
parseSemicolon();
const node = factory.createImportEqualsDeclaration(decorators, modifiers, identifier, moduleReference);
const node = factory.createImportEqualsDeclaration(decorators, modifiers, isTypeOnly, identifier, moduleReference);
const finished = withJSDoc(finishNode(node, pos), hasJSDoc);
if (isTypeOnly) {
parseErrorAtRange(finished, Diagnostics.Only_ECMAScript_imports_may_use_import_type);
}
return finished;
}

Expand Down
1 change: 1 addition & 0 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ namespace ts {
decl,
/*decorators*/ undefined,
decl.modifiers,
decl.isTypeOnly,
decl.name,
factory.updateExternalModuleReference(decl.moduleReference, rewriteModuleSpecifier(decl, specifier))
);
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2973,6 +2973,11 @@ namespace ts {
* @param node The import equals declaration node.
*/
function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult<Statement> {
// Always elide type-only imports
if (node.isTypeOnly) {
return undefined;
}

if (isExternalModuleImportEqualsDeclaration(node)) {
const isReferenced = resolver.isReferencedAliasDeclaration(node);
// If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'.
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2925,6 +2925,7 @@ namespace ts {
readonly kind: SyntaxKind.ImportEqualsDeclaration;
readonly parent: SourceFile | ModuleBlock;
readonly name: Identifier;
readonly isTypeOnly: boolean;

// 'EntityName' for an internal module reference, 'ExternalModuleReference' for an external
// module reference.
Expand Down Expand Up @@ -3037,6 +3038,7 @@ namespace ts {

export type TypeOnlyCompatibleAliasDeclaration =
| ImportClause
| ImportEqualsDeclaration
| NamespaceImport
| ImportOrExportSpecifier
;
Expand Down Expand Up @@ -7000,8 +7002,8 @@ namespace ts {
updateCaseBlock(node: CaseBlock, clauses: readonly CaseOrDefaultClause[]): CaseBlock;
createNamespaceExportDeclaration(name: string | Identifier): NamespaceExportDeclaration;
updateNamespaceExportDeclaration(node: NamespaceExportDeclaration, name: Identifier): NamespaceExportDeclaration;
createImportEqualsDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: string | Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration;
updateImportEqualsDeclaration(node: ImportEqualsDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration;
createImportEqualsDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, isTypeOnly: boolean, name: string | Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration;
updateImportEqualsDeclaration(node: ImportEqualsDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, isTypeOnly: boolean, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration;
createImportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression): ImportDeclaration;
updateImportDeclaration(node: ImportDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression): ImportDeclaration;
createImportClause(isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause;
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/utilitiesPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,8 @@ namespace ts {
case SyntaxKind.NamespaceImport:
return (node as NamespaceImport).parent.isTypeOnly;
case SyntaxKind.ImportClause:
return (node as ImportClause).isTypeOnly;
case SyntaxKind.ImportEqualsDeclaration:
return (node as ImportClause | ImportEqualsDeclaration).isTypeOnly;
default:
return false;
}
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/visitorPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,7 @@ namespace ts {
return factory.updateImportEqualsDeclaration(<ImportEqualsDeclaration>node,
nodesVisitor((<ImportEqualsDeclaration>node).decorators, visitor, isDecorator),
nodesVisitor((<ImportEqualsDeclaration>node).modifiers, visitor, isModifier),
(<ImportEqualsDeclaration>node).isTypeOnly,
nodeVisitor((<ImportEqualsDeclaration>node).name, visitor, isIdentifier),
nodeVisitor((<ImportEqualsDeclaration>node).moduleReference, visitor, isModuleReference));

Expand All @@ -949,7 +950,7 @@ namespace ts {

case SyntaxKind.ImportClause:
return factory.updateImportClause(<ImportClause>node,
(node as ImportClause).isTypeOnly,
(<ImportClause>node).isTypeOnly,
Comment thread
andrewbranch marked this conversation as resolved.
nodeVisitor((<ImportClause>node).name, visitor, isIdentifier),
nodeVisitor((<ImportClause>node).namedBindings, visitor, isNamedImportBindings));

Expand Down Expand Up @@ -980,7 +981,7 @@ namespace ts {
return factory.updateExportDeclaration(<ExportDeclaration>node,
nodesVisitor((<ExportDeclaration>node).decorators, visitor, isDecorator),
nodesVisitor((<ExportDeclaration>node).modifiers, visitor, isModifier),
(node as ExportDeclaration).isTypeOnly,
(<ExportDeclaration>node).isTypeOnly,
nodeVisitor((<ExportDeclaration>node).exportClause, visitor, isNamedExportBindings),
nodeVisitor((<ExportDeclaration>node).moduleSpecifier, visitor, isExpression));

Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/convertToTypeOnlyImport.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @internal */
namespace ts.codefix {
const errorCodes = [Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValues_is_set_to_error.code];
const errorCodes = [Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_importsNotUsedAsValues_is_set_to_error.code];
const fixId = "convertToTypeOnlyImport";
registerCodeFix({
errorCodes,
Expand Down
1 change: 1 addition & 0 deletions src/services/codefixes/fixInvalidImportSyntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace ts.codefix {
variations.push(createAction(context, sourceFile, node, factory.createImportEqualsDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*isTypeOnly*/ false,
namespace.name,
factory.createExternalModuleReference(node.moduleSpecifier)
)));
Expand Down
1 change: 1 addition & 0 deletions src/services/codefixes/importFixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@ namespace ts.codefix {
? factory.createImportEqualsDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
typeOnly,
factory.createIdentifier(namespaceLikeImport.name),
factory.createExternalModuleReference(quotedModuleSpecifier))
: factory.createImportDeclaration(
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/requireInTs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace ts.codefix {
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info) {
const { allowSyntheticDefaults, defaultImportName, namedImports, statement, required } = info;
changes.replaceNode(sourceFile, statement, defaultImportName && !allowSyntheticDefaults
? factory.createImportEqualsDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, defaultImportName, factory.createExternalModuleReference(required))
? factory.createImportEqualsDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*isTypeOnly*/ false, defaultImportName, factory.createExternalModuleReference(required))
: factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, factory.createImportClause(/*isTypeOnly*/ false, defaultImportName, namedImports), required));
}

Expand Down
Loading