These changes introduce the new `@Service` decorator which is a more ergonomic alternative to `@Injectable`. The reason we're adding a new decorator is that `@Injectable` has been around since the beginning of Angular and it has a lot of baggage that adds unnecessary overhead for users that generally want to define a singleton service, available in their entire app. The key differences between `@Service` and `@Injectable` are:
1. `@Service` is `providedIn: 'root'` by default. You can opt into providing the service yourself by setting `autoProvided: false` on it.
2. `@Service` doesn't allow constructor-based injection, only the `inject` function.
3. `@Service` doesn't support the complex type signature of `@Injectable` (`useClass`, `useValue` etc.). Instead it supports a single `factory` function.
Example:
```ts
import {Service} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {AuthService} from './auth';
@Service()
export class PostService {
private readonly httpClient = inject(HttpClient);
private readonly authService = inject(AuthService);
getUserPosts() {
return this.httpClient.get('/api/posts/' + this.authService.userId);
}
}
```
Moves the logic for generating type check blocks into the compiler since it isn't coupled to TypeScript anymore.
Note: the tests haven't been moved over, because they depend on the environment that's currently in `compiler-cli` and it still has some dependencies on TypeScript.
To support the need to resolve symbols without full AST access (e.g. when using virtual files), this commit decouples `ReferenceSymbol` from `ts.ClassDeclaration`.
Changes:
- Updated `ReferenceSymbol.target` to use `SymbolReference` instead of `ts.ClassDeclaration`.
- Removed `getReferenceTargetNode()` from `SymbolDirectiveMeta` and transitioned to `getSymbolReference()`.
- Refactored `getTsSymbolOfReference` in `checker.ts` to handle `SymbolReference` and resolve it to a `ts.Symbol` using a position-optimized AST traversal. This avoids using the private `getTokenAtPosition` API and avoids full file scans by only traversing nodes containing the target position.
The commit updates the TCB for safe navigation expressions to allow for correct narrowing of nullables.
This will trigger the `nullishCoalescingNotNullable` and `optionalChainNotNullable` diagnostics on exisiting projects.
You might want to disable those 2 diagnotiscs in your `tsconfig` temporarily if you want to update your project without having to fix all the issues at once.
Narrowing can be disabled altogether with `strictSafeNavigationTyes: false`.
fixes#37619
BREAKING CHANGE: This change will trigger the `nullishCoalescingNotNullable` and `optionalChainNotNullable` diagnostics on exisiting projects.
You might want to disable those 2 diagnotiscs in your `tsconfig` temporarily.
Decouple `SymbolBuilder` from the full `BoundTarget` interface by introducing a purpose-built `SymbolBoundTarget` interface containing only the 4 methods required for symbol resolution. This eliminates the need for the large, pass-through `BoundTargetAdapter` and further isolates `SymbolBuilder` from compiler-internal implementation details.
Also minimize `TypeCheckableDirectiveMetaAdapter` by redefining `SymbolDirectiveMeta` to not extend `DirectiveMeta`, exposing only the properties actually used by `SymbolBuilder`.
Removed dead code `getDirectiveMeta` in `template_symbol_builder.ts` which was unused.
These changes improve maintainability and ensure a cleaner architecture by strictly defining the boundaries of what `SymbolBuilder` needs from the rest of the system.
By limiting the required inputs to only what's necessary for the implementation, we make it easier to re-use
the implementation between different compiler architectures
removing ts.typechecker in a prior refactor caused some regressions, particularly when multiple directives
appear on a single elemnt. this is now addressed by using an id for directives and storing that in the tcb comment
Fixes an error that was heppning when a generic param has type parameters of its own. There were a few different issues going on:
1. In #67707 I had changed a bit how we pass the `genericContextBehavior` which ended up ignoring the `useContextGenericType` option from the environment.
2. All directives depend on themselves, but we were overridding the `genericContextBehavior` for the directive being processed.
3. The type translator wasn't handling type parameter declarations. Technically we shouldn't be able to hit a code path that has a type parameter, however it's also easy enough to handle so we might as well.
Relates to #67704.
- Fixes TemplateSymbolBuilder.getTcbPositionForNode to recursively unwrap AsExpression and NonNullExpression nodes.
- Added a test case in type_checker__get_symbol_of_template_node_spec.ts to verify symbol mapping when strictSafeNavigationTypes is false.
- Note: The issue likely broke in commit 13c8df67d9.
This updates the SymbolBuilder to no longer use ts.TypeChecker internally to
build symbols for the language service. These lookups are deferred/done later
using the newly expanded template type checker API.
Implements the logic at the compiler level that will de-duplicate host directives and merge them together. It will also report if a conflict is detected during merging.
Moves the `ClassPropertyMapping` into the compiler, rather than having to pass around the limited `InputOutputPropertySet` interface that is only implemented by `ClassPropertyMapping`.
Previously, the Language Service fetched Quick Info and definitions for template variables (such as `@let` declarations) using mapping to their `initializerLocation` (the right-hand side expression). This aggressively bubbled the type, JSDoc, and definition identity of the initializer backwards onto the variable itself.
This approach had two flaws:
1. It broke type narrowing because the LS read the original un-narrowed type from the source expression rather than the type of the narrowed intermediate variable in the Type Check Block.
2. It deviated from native TypeScript semantics, where a local `let` binding (`let address = hero.address`) does not inherit the docstrings or `(property)` kind of its initializer, acting solely as a local inferred variable.
By using `localVarLocation` rather than `initializerLocation` for LetDeclaration Quick Info and Type Definitions, these intermediate variables now properly preserve type narrowing within templates and flawlessly match the standard behavior expected of TypeScript block variables. `VariableSymbol.initializerLocation` is retained solely to map the value spans of structural directive contexts (e.g., `exportAs` strings).
fixes#65491
Currently the `OutOfBandDiagnosticRecorder` is tied to producing TypeScript diagnostics, however some of our use cases might call for a different form.
These changes decouple the recorder from TypeScript and make the diagnostic type generic.
Add NG8023 extended diagnostic to report duplicate component selectors
during compilation.
This replaces the former NG0300 runtime error, ensuring the failure
occurs at build time instead of runtime.
Closes angular#48377
BREAKING CHANGE: Elements with multiple matching selectors will now throw at compile time.
`<input type="number">` often does not provide the desired user experience when editing numbers in
a form. MDN even [describes](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/number#using_number_inputs)
how text inputs should be used in many cases instead, via `<input type="text" inputmode="numeric">`
or similar configurations. Previously, this did not work with Signal Forms without a custom input
component/directive.
This PR builds support for binding `number|null` models directly to `<input type="text">` native
controls via `[formField]`. When a model has a number or `null` value, signal forms will preserve
that status when the user makes edits/changes. Empty string values are converted to `null`, other
values are parsed as numbers, and a parse error is raised when a non-numeric value is entered.
Note that it's up to the UI developer to configure additional UI affordances such as setting an
appropriate `inputmode`, rejecting non-numeric keypresses, etc.
Fixes#66903Fixes#66157
Fixes a regression caused by the recent TCB changes where we moved the type parameter processing earlier in the pipeline and stopped properly accounting for the `TcbGenericContextBehavior`.
Fixes#67704.
Optimization Goals:
The primary goals of this optimization are to dramatically reduce the execution time of the language-service test suite and stabilize the mock file system infrastructure. Previously, the suite suffered from significant overhead due to recreating the `MockFileSystem`, `MockServerHost`, and TypeScript `ProjectService` for every single test block, leading to redundant parsing operations, slow test initialization, and reduced spec performance.
How the Goals Were Achieved:
1. **Mock File System Optimizations (Shared State)**:
- Evaluated that standard test files (e.g., TS/Angular lib definitions) are immutable across tests.
- Introduced and utilized `lockMockFileSystem()` to initialize the mock `FileSystem` with `loadStandardTestFiles()` only once per test suite run rather than repeatedly per test.
- Refactored `LanguageServiceTestEnv.setup()` to reuse the singleton file system, completely skipping redundant module loading by eagerly flagging `fsInitialized = true`.
2. **Language Service Test Environment Enhancements (TypeScript Project Reuse)**:
- Implemented partial configuration reloads in the `Project` class via the `update()` method, removing the need to tear down and rebuild the entire `MockServerHost` and TypeScript `ProjectService` from scratch when minimal file changes (like HTML templates or local TS edits) are made dynamically by a test.
- Applied `projectService.reloadProjects()` and `scriptInfo.reloadFromFile()` to synchronously push mock file tree invalidations to the active TS program, skipping expensive environment initialization and saving considerable latency across tests.
- Added `projectName` identifiers inside complex isolate tests (e.g. module alias aliasing) so custom environment injections can sandbox safely without invalidating the global default environment cache.
3. **Test Suite Unification**:
- Flattened fragmented test groups (`grp1`, `grp3`, `grp4`) into a cohesive single directory at `packages/language-service/test/`. This simplifies execution config, improves test runner concurrency, and unifies local development targeting.
- Cleaned out broken inline debug logging and unneeded config reloading loops.
4. **Maintaining Test Isolation**:
- **Explicit TypeScript Configuration**: While the underlying `MockFileSystem` ("disk") is aggressively reused across tests, the TypeScript `ProjectService` and its execution environment are entirely recreated for every test run to ensure isolated ASTs and module resolution caches.
- **Strict tsconfig.json Files Array**: When a project is initialized, it explicitly defines its boundary using the strict `files: [ ... ]` array in `tsconfig.json`. This ensures that any leftover files physically on the mock disk from an older test run are completely invisible to the TS Compiler.
- **Namespace Sandboxing**: For tests doing custom modifications (e.g., overriding module resolution paths), they utilize localized `projectName` arguments (like `"test_alias_completions"`) to configure sandboxed working directories.
In some cases the `debugName` transform generates a spread into the signal function parameters. This can cause compiler errors, because the functions don't have rest parameters.
These changes work around it by adding a `@ts-ignore` above it.
This commit refactors the template type checking metadata interfaces to use detached, serializable metadata rather than retaining direct references to ts.Node or ts.Declaration instances.
A new tcb_adapter translates traditional TypeScript AST-bound metadata into these decoupled structures. This abstraction lays the groundwork for supporting native preprocessors (such as Rust or ts-go) which serialize metadata over JSON rather than passing live TypeScript objects.
Key changes:
- Introduced TcbDirectiveMetadata, TcbComponentMetadata, TcbReferenceMetadata, and TcbPipeMetadata to replace TypeCheckableDirectiveMeta where appropriate.
- Substituted deep TS compilation AST references with string module names and source spans to preserve out-of-band diagnostic capabilities.
- Detached generic typeParameters and transformType properties into synthesized, standalone TS mappings.
- Updated generateTypeCheckBlock and corresponding Operations to consume the new metadata.
The Angular compiler unconditionally adds a debug name transform for signals
which generates a conditional on `ngDevMode` (e.g., `ngDevMode ? { debugName: "xyz" } : []`).
During testing, `ngDevMode` is true, so the true branch executes but the
false branch is never executed. Consequently, coverage tools report the
false branch as an untested line/branch, preventing 100% test coverage.
This commit adds a synthetic `/* istanbul ignore next */` comment to the
generated false branch so that Istanbul ignores it. We only include the
istanbul comment (instead of additionally including c8) to focus on the
established standard for Angular CLI/Karma coverage while maintaining
compatibility with modern Vitest setups, since @vitest/coverage-v8 now
natively respects the fallback istanbul comment.
Fixes#64583
This is a follow-up to #67381 which introduced a subtle bug where depending on the type checking configuration, we may put an object literal directly in the TCB body which the TS compiler ends up interpreting as a block. These changes resolve the issue by always wrapping the literal in parentheses.