diff --git a/packages/angular/build/src/tools/angular/transformers/jit-bootstrap-transformer.ts b/packages/angular/build/src/tools/angular/transformers/jit-bootstrap-transformer.ts index d1d0a76df85d..68f572f2e8b6 100644 --- a/packages/angular/build/src/tools/angular/transformers/jit-bootstrap-transformer.ts +++ b/packages/angular/build/src/tools/angular/transformers/jit-bootstrap-transformer.ts @@ -142,6 +142,17 @@ export function elideImports( return; } + // Pure type declarations and type-only exports are erased at runtime. + if ( + ts.isInterfaceDeclaration(node) || + ts.isTypeAliasDeclaration(node) || + ts.isTypeParameterDeclaration(node) || + (ts.isExportDeclaration(node) && node.isTypeOnly) || + (ts.isExportSpecifier(node) && node.isTypeOnly) + ) { + return; + } + let symbol: ts.Symbol | undefined; switch (node.kind) { case ts.SyntaxKind.Identifier: diff --git a/packages/ngtools/webpack/src/transformers/elide_imports.ts b/packages/ngtools/webpack/src/transformers/elide_imports.ts index 856511db165e..e0f109b78f9e 100644 --- a/packages/ngtools/webpack/src/transformers/elide_imports.ts +++ b/packages/ngtools/webpack/src/transformers/elide_imports.ts @@ -59,6 +59,17 @@ export function elideImports( return; } + // Pure type declarations and type-only exports are erased at runtime. + if ( + ts.isInterfaceDeclaration(node) || + ts.isTypeAliasDeclaration(node) || + ts.isTypeParameterDeclaration(node) || + (ts.isExportDeclaration(node) && node.isTypeOnly) || + (ts.isExportSpecifier(node) && node.isTypeOnly) + ) { + return; + } + let symbol: ts.Symbol | undefined; switch (node.kind) { case ts.SyntaxKind.Identifier: diff --git a/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts b/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts index 92d28e46d8cd..8c9f73f6f471 100644 --- a/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts +++ b/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts @@ -414,6 +414,47 @@ describe('elide_imports', () => { expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); }); + + it('should remove import when specifiers are only used in type annotations, type-only re-exports, or interface extends', () => { + const input = tags.stripIndent` + import { Decorator } from './decorator'; + import { OnChanges, SimpleChanges } from './type'; + import { Service, Service2 } from './service'; + + export type { SimpleChanges }; + export { type Service2 }; + export interface ExtendedChanges extends OnChanges { + extra: string; + } + + @Decorator() + export class Foo { + constructor(param: Service) { + } + } + + ${dummyNode} + `; + + const output = tags.stripIndent` + import { __decorate } from "tslib"; + import { Decorator } from './decorator'; + + let Foo = class Foo { constructor(param) { } }; + Foo = __decorate([ Decorator() ], Foo); + export { Foo }; + `; + + const { program, compilerHost } = createTypescriptContext( + input, + additionalFiles, + true, + extraCompilerOptions, + ); + const result = transformTypescript(undefined, [transformer(program)], program, compilerHost); + + expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); + }); }); it('keeps jsxFactory imports when configured', () => {