With #504, OXC finds Angular class decorators by their @angular/core import, so an aliased @Injectable is now compiled. ngtsc 22.1.7 compiles it differently. It looks like an ngtsc bug. This issue records the difference and asks whether we keep OXC's output.
Repro
import {Injectable as X} from '@angular/core';
@X({providedIn: 'root'})
export class S {}
ngtsc 22.1.7
export class S {
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: S, factory: S.ɵfac, providedIn: 'root' });
}
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
No ɵfac and no setClassMetadata. ɵprov.factory points at S.ɵfac, which doesn't exist, so injecting S fails at runtime.
Cause, in packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts:
needsFactory:
meta.decorators === null ||
decorators.every((current) => !isAngularCore(current) || current.name === 'Injectable'),
current.name is the name as written (X), so an aliased import gives needsFactory = false. setClassMetadata is only emitted inside if (analysis.needsFactory), so it is dropped too.
OXC (#504 tip)
export class S {
static ɵfac = /*@__PURE__*/ (() =>{
let ɵS_BaseFactory;
return function S_Factory(__ngFactoryType__) {
return (ɵS_BaseFactory || (ɵS_BaseFactory = i0.ɵɵgetInheritedFactory(S)))((__ngFactoryType__ || S));
};
})();
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({token:S,factory:S.ɵfac,providedIn:"root"});
}
// + ɵsetClassMetadata(S,[{type:X,args:[{providedIn:"root"}]}],null,null)
static ɵfac: i0.ɵɵFactoryDeclaration<S, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<S>;
On main the aliased decorator is not recognized at all, so the class is left as written (@X({providedIn: 'root'}) export class S {}).
Decision needed
- Keep OXC's output (a working factory). This is a known difference from ngtsc. Add it to
napi/angular-compiler/e2e/compare/fixtures/known-differences.ts.
- Or copy ngtsc's output exactly. That gives a broken
ɵprov.
Recommendation: keep it, record the difference, and maybe report it upstream.
Note: the ɵgetInheritedFactory form and InjectableDeclaration<S> vs <any> also show up for a plain @Injectable() class with no constructor, on main too. Not specific to the alias.
With #504, OXC finds Angular class decorators by their
@angular/coreimport, so an aliased@Injectableis now compiled. ngtsc 22.1.7 compiles it differently. It looks like an ngtsc bug. This issue records the difference and asks whether we keep OXC's output.Repro
ngtsc 22.1.7
No
ɵfacand nosetClassMetadata.ɵprov.factorypoints atS.ɵfac, which doesn't exist, so injectingSfails at runtime.Cause, in
packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts:current.nameis the name as written (X), so an aliased import givesneedsFactory = false.setClassMetadatais only emitted insideif (analysis.needsFactory), so it is dropped too.OXC (#504 tip)
On main the aliased decorator is not recognized at all, so the class is left as written (
@X({providedIn: 'root'}) export class S {}).Decision needed
napi/angular-compiler/e2e/compare/fixtures/known-differences.ts.ɵprov.Recommendation: keep it, record the difference, and maybe report it upstream.
Note: the
ɵgetInheritedFactoryform andInjectableDeclaration<S>vs<any>also show up for a plain@Injectable()class with no constructor, on main too. Not specific to the alias.