fix(di): resolve T | null constructor param types as injection tokens#297
Merged
Conversation
`@Optional() svc: MyService | null` (and the bare `svc: MyService | null` variant) silently emitted `ɵɵinvalidFactoryDep`, which throws at instantiation. All five token extractors and both class-metadata helpers only matched bare `TSTypeReference`, so any union annotation fell through. Adds `util::resolve_di_token_type` which mirrors Angular's `typeReferenceToExpression`: filters `null` keyword variants out of unions and narrows to the sole remaining type, while transparently unwrapping `TSParenthesizedType` so `(MyService | null)`, `(MyService) | null`, and `MyService | (null)` all resolve. `undefined` is intentionally preserved to match the reference compiler. Closes #285 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
T | nullconstructor param types not narrowed for token resolution #285.@Optional() svc: MyService | null(and the baresvc: MyService | nullvariant) silently emittedɵɵinvalidFactoryDep, which throws at instantiation — the canonical optional-DI pattern was unusable.extract_param_token(component / directive / injectable / pipe / ng_module) plus two helpers inclass_metadata/builders.rsonly matched bareTSTypeReference. Any union annotation fell through toNone.util::resolve_di_token_typethat mirrors Angular'stypeReferenceToExpressionincompiler-cli/src/ngtsc/reflection/src/typescript.ts— filtersnullkeyword variants out of unions and narrows to the sole remaining type, transparently unwrappingTSParenthesizedTypeso(MyService | null),(MyService) | null, andMyService | (null)all resolve.undefinedis intentionally preserved to keepT | undefinedambiguous, matching the reference.Test plan
T | null,null | T, parenthesized forms, with and without@Optional().cargo test -p oxc_angular_compiler— 1044 lib tests pass (+ all integration tests).cargo run -p oxc_angular_conformance— 1252/1252 conformance cases pass.?: Tsuffix syntax) still pass; no behavior change for non-union annotations.🤖 Generated with Claude Code
Note
Medium Risk
Touches DI token resolution across all Angular decorator kinds and class metadata; behavior change is scoped to union/nullable annotations but incorrect narrowing could still mis-emit factory dependencies.
Overview
Fixes #285: constructor params typed as
MyService | null(with or without@Optional()) no longer lose their injection token and emitɵɵinvalidFactoryDepat runtime.A shared
resolve_di_token_typehelper (aligned with Angular’s referencetypeReferenceToExpression) stripsnullfrom unions, unwraps parenthesized types, and leavesT | undefinedambiguous. Allextract_param_tokenpaths (component, directive, injectable, pipe, ng_module) and class metadata type extraction now use it instead of matching only bareTSTypeReference.Regression tests cover union order, optional vs non-optional, and parenthesized forms across decorator kinds.
Reviewed by Cursor Bugbot for commit 78dd5b7. Bugbot is set up for automated code reviews on this repo. Configure here.