This commit extends the template targeting system, which determines the node
being referenced given a template position, to return additional context if
needed about the particular aspect of the node to which the position refers.
For example, a position pointing to an element node may be pointing either
to its tag name or to somewhere in the node body. This is the difference
between `<div|>` and `<div foo | bar>`.
PR Close#40032
The vim editor produces temporarily files that can end in both .swo and
.swp. This commits add .swp to the .gitignore so we don't accidentaly
commit temporary files.
PR Close#40094
Projects opened in the LS are often larger in scope than the compilation
units seen by the compiler when actually building. For example, in the LS
it's not uncommon for the project to include both application as well as
test files. This can create issues when the combination of files results
in errors that are not otherwise present - for example, if test files
have inline NgModules that re-declare components (a common Angular pattern).
Such code is valid when compiling the app only (test files are excluded, so
only one declaration is seen by the compiler) or when compiling tests only
(since tests run in JIT mode and are not seen by the AOT compiler), but when
both sets of files are mixed into a single compilation unit, the compiler
sees the double declaration as an error.
This commit attempts to mitigate the problem by forcing the compiler flag
`compileNonExportedClasses` to `false` in a LS context. When tests contain
duplicate declarations, often such declarations are inline in specs and not
exported from the top level, so this flag can be used to greatly improve the
IDE experience.
PR Close#40092
When `checkTypeOfPipes` is set to `false`, the configuration is meant to
ignore the signature of the pipe's `transform` method for diagnostics.
However, we still should produce some information about the pipe for the
`TemplateTypeChecker`. This change refactors the returned symbol for
pipes so that it also includes information about the pipe's class
instance as it appears in the TCB.
PR Close#39555
The TCB utility functions used to find nodes in the TCB are currently
configured to ignore results when an ignore marker is found. However,
these ignore markers are only meant to affect diagnostics requests. The
Language Service may have a need to find nodes with diagnostic ignore
markers. The most common example of this would be finding references for
generic directives. The reference appears to the generic directive's
class appears on the type ctor in the TCB, which is ignored for
diagnostic purposes.
These functions should only skip results when the request is in the
context of a larger request for _diagnostics_. In all other cases, we
should get matches, even if a diagnostic ignore marker is encountered.
PR Close#40071
This commit adds the ability to find references for a directive or component
from within a component template. That is, you can find component references
from the element tag `<my-c|omp></my-comp>` (where `|` is the cursor position)
as well as find references for directives that match a given attribute
`<div d|ir></div>`.
PR Close#40054
If we've already identified that we are within a `keySpan` of a node, we
exit the visitor logic early. It can be the case that we have two nodes
which technically match a given location when the end span of one node
touches the start of the keySpan for the candidate node. Because
our `isWithin` logic is inclusive on both ends, we can match both nodes.
This change exits the visitor logic once we've identified a node where
the position is within its `keySpan`.
PR Close#40047
The visitor has a check in it with the goal of preventing the structural directive
parent elements from matching when we have already found the candidate we want.
However, this code did not check to ensure that it was looking at the correct
type of node for this case and was evaluating this logic in places it shouldn't.
This special check can be more easily done by simply not traversing the
template children if we've already found a candidate on the template
node itself.
PR Close#40047
This commit adds support to the Language Service for autocompletion within
expression contexts. Specifically, this is auto completion of property reads
and method calls, both in normal and safe-navigational forms.
PR Close#39727
When `checkTypeOfOutputEvents` is `false`, we still need to produce the access
to the `EventEmitter` so the Language Service can still get the
type information about the field. That is, in a template `<div
(output)="handle($event)"`, we still want to be able to grab information
when the cursor is inside the "output" parens. The flag is intended only
to affect whether the compiler produces diagnostics for the inferred
type of the `$event`.
PR Close#39515
Rather than returning `null`, we can provide some useful information to the Language Service
by returning a symbol for the `addEventListener` function call when the consumer
of a binding as an element.
PR Close#39312
When the compiler option `checkTypeOfAttributes` is `false`, we should
still be able to produce type information from the
`TemplateTypeChecker`. The current behavior ignores all attributes that
map to directive inputs. This commit includes those attribute bindings
in the TCB but adds the "ignore for diagnostics" marker so they do not
produce errors. This way, consumers of the TTC (the Language Service)
can still get valid information about these attributes even when the
user has configured the compiler to not produce diagnostics/errors for them.
PR Close#39537
This commit adds support in the Ivy Language Service for autocompletion in a
global context - e.g. a {{foo|}} completion.
Support is added both for the primary function `getCompletionsAtPosition` as
well as the detail functions `getCompletionEntryDetails` and
`getCompletionEntrySymbol`. These latter operations are not used yet as an
upstream change to the extension is required to advertise and support this
capability.
PR Close#39250
In the past, the legacy (VE-based) language service would use a
`UrlResolver` instance to resolve file paths, primarily for compiler
resources like external templates. The problem with this is that the
UrlResolver is designed to resolve URLs in general, and so for a path
like `/a/b/#c`, `#c` is treated as hash/fragment rather than as part
of the path, which can lead to unexpected path resolution (f.x.,
`resolve('a/b/#c/d.ts', './d.html')` would produce `'a/b/d.html'` rather
than the expected `'a/b/#c/d.html'`).
This commit resolves the issue by using Node's `path` module to resolve
file paths directly, which aligns more with how resources are resolved
in the Ivy compiler.
The testing story here is not great, and the API for validating a file
path could be a little bit prettier/robust. However, since the VE-based
language service is going into more of a "maintenance mode" now that
there is a clear path for the Ivy-based LS moving forward, I think it is
okay not to spend too much time here.
Closes https://github.com/angular/vscode-ng-language-service/issues/892
Closes https://github.com/angular/vscode-ng-language-service/issues/1001
PR Close#39917
Create stubs for `findRenameLocations` for both VE and Ivy Language Service
implementations. This will prevent failed requests when it is implemented on the vscode plugin side.
PR Close#39919
When the compiler is invoked via ngc or the Angular CLI, its APIs are used
under the assumption that Angular analysis/diagnostics are only requested if
the program has no TypeScript-level errors. A result of this assumption is
that the incremental engine has not needed to resolve changes via its
dependency graph when the program contained broken imports, since broken
imports are a TypeScript error.
The Angular Language Service for Ivy is using the compiler as a backend, and
exercising its incremental compilation APIs without enforcing this
assumption. As a result, the Language Service has run into issues where
broken imports cause incremental compilation to fail and produce incorrect
results.
This commit introduces a mechanism within the compiler to keep track of
files for which dependency analysis has failed, and to always treat such
files as potentially affected by future incremental steps. This is tested
via the Language Service infrastructure to ensure that the compiler is doing
the right thing in the case of invalid imports.
PR Close#39923
To avoid overwhelming a user with secondary diagnostics that derive from a
"root cause" error, the compiler has the notion of a "poisoned" NgModule.
An NgModule becomes poisoned when its declaration contains semantic errors:
declarations which are not components or pipes, imports which are not other
NgModules, etc. An NgModule also becomes poisoned if it imports or exports
another poisoned NgModule.
Previously, the compiler tracked this poisoned status as an alternate state
for each scope. Either a correct scope could be produced, or the entire
scope would be set to a sentinel error value. This meant that the compiler
would not track any information about a scope that was determined to be in
error.
This method presents several issues:
1. The compiler is unable to support the language service and return results
when a component or its module scope is poisoned.
This is fine for compilation, since diagnostics will be produced showing the
error(s), but the language service needs to still work for incorrect code.
2. `getComponentScopes()` does not return components with a poisoned scope,
which interferes with resource tracking of incremental builds.
If the component isn't included in that list, then the NgModule for it will
not have its dependencies properly tracked, and this can cause future
incremental build steps to produce incorrect results.
This commit changes the tracking of poisoned module scopes to use a flag on
the scope itself, rather than a sentinel value that replaces the scope. This
means that the scope itself will still be tracked, even if it contains
semantic errors. A test is added to the language service which verifies that
poisoned scopes can still be used in template type-checking.
PR Close#39923
The Language Service "find references" currently uses the
`ngtypecheck.ts` suffix to determine if a file is a shim file. Instead,
a better API would be to expose a method in the template type checker
that does this verification so that the LS does not have to "know" about
the typecheck suffix. This also fixes an issue (albeit unlikely) whereby a file
in the user's program that _actually_ is named with the `ngtypecheck.ts`
suffix would have been interpreted as a shim file.
PR Close#39768
This commit adds "find references" functionality to the Ivy integrated
language service. The basic approach is as follows:
1. Generate shims for all files to ensure we find references in shims
throughout the entire program
2. Determine if the position for the reference request is within a
template.
* Yes, it is in a template: Find which node in the template AST the
position refers to. Then find the position in the shim file for that
template node. Pass the shim file and position in the shim file along
to step 3.
* No, the request for references was made outside a template: Forward
the file and position to step 3.
3. (`getReferencesAtTypescriptPosition`): Call the native TypeScript LS
`getReferencesAtPosition`. For each reference that is in a shim file, map those
back to a template location, otherwise return it as-is.
PR Close#39768
Create stubs for getTypeDefinitionAtPosition for both VE and Ivy Language Service
implementations. This will prevent failed requests when it is implemented on the vscode plugin side.
PR Close#39829
We need to expose the declaration files for Ivy sources so that they can
be consumed by the Angular language server (`@angular/language-server`).
PR Close#39748
Both `ReferenceSymbol` and `VariableSymbol` have two locations of
interest to an external consumer.
1. The location for the initializers of the local TCB variables allow consumers
to query the TypeScript Language Service for information about the initialized type of the variable.
2. The location of the local variable itself (i.e. `_t1`) allows
consumers to query the TypeScript LS for references to that variable
from within the template.
PR Close#39715
language_service_adapter_spec was renamed to adapters_spec as part of
d39c4bbe37, but I failed to check in
adapters_spec, thereby just deleting the spec. This reintroduces it.
PR Close#39742
Currently `readConfiguration` relies on the file system to perform disk
utilities needed to read determine a project configuration file and read
it. This poses a challenge for the language service, which would like to
use `readConfiguration` to watch and read configurations dependent on
extended tsconfigs (#39134). Challenges are at least twofold:
1. To test this, the langauge service would need to provide to the
compiler a mock file system.
2. The language service uses file system utilities primarily through
TypeScript's `Project` abstraction. In general this should correspond
to the underlying file system, but it may differ and it is better to
go through one channel when possible.
This patch alleviates the concern by directly providing to the compiler
a "ParseConfigurationHost" with read-only "file system"-like utilties.
For the language service, this host is derived from the project owned by
the language service.
For more discussion see
https://docs.google.com/document/d/1TrbT-m7bqyYZICmZYHjnJ7NG9Vzt5Rd967h43Qx8jw0/edit?usp=sharing
PR Close#39619
This commit adds new language service testing infrastructure which allows
for in-memory testing. It solves a number of issues with the previous
testing infrastructure that relied on a single integration project across
all of the tests, and also provides for much faster builds by using
the compiler-cli's mock versions of @angular/core and @angular/common.
A new `LanguageServiceTestEnvironment` class (conceptually mirroring the
compiler-cli `NgtscTestEnvironment`) controls setup and execution of tests.
The `FileSystem` abstraction is used to drive a `ts.server.ServerHost`,
which backs the language service infrastructure.
Since many language service tests revolve around the template, the API is
currently optimized to spin up a "skeleton" project and then override its
template for each test.
The existing Quick Info tests (quick_info_spec.ts) were ported to the new
infrastructure for validation. The tests were cleaned up a bit to remove
unnecessary initializations as well as correct legitimate template errors
which did not affect the test outcome, but caused additional validation of
test correctness to fail. They still utilize a shared project with all
fields required for each individual unit test, which is an anti-pattern, but
new tests can now easily be written independently without relying on the
shared project, which was extremely difficult previously. Future cleanup
work might refactor these tests to be more independent.
PR Close#39594
In preparation for in-memory testing infrastructure, the existing Ivy
language service tests are moved to a `legacy` directory. These existing
tests rely on a single integration project in `test/project/app`, which
presents a number of challenges:
* adding extra fields/properties to the integration project for one test
can cause others to fail/flake.
* it's especially difficult to test any cases that require introducing
intentional errors, as those tend to break other tests.
* tests load files from disk, which is slower.
* tests rely on the real built versions of @angular/core and
@angular/common, which makes them both slow to build and require rebuilds
on every compiler change.
* tests share a single tsconfig.json, making it extremely difficult to test
how the language service handles different configuration scenarios (e.g.
different type-checking flags).
PR Close#39594
Similar to #39613, #39609, and #38898, we should store the `keySpan` for
Reference nodes so that we can accurately map from a template node to a
span in the original file. This is most notably an issue at the moment
for directive references `#ref="exportAs"`. The current behavior for the
language service when requesting information for the reference
is that it will return a text span that results in
highlighting the entire source when it should only highlight "ref" (test
added for this case as well).
PR Close#39616
Though we currently have the knowledge of where the `key` for an
event binding appears during parsing, we do not propagate this
information to the output AST. This means that once we produce the
template AST, we have no way of mapping a template position to the key
span alone. The best we can currently do is map back to the
`sourceSpan`. This presents problems downstream, specifically for the
language service, where we cannot provide correct information about a
position in a template because the AST is not granular enough.
This is essentially identical to the change from #38898, but for event
bindings rather than input bindings.
PR Close#39609
Similar to #39609 and #38898, though we currently have the knowledge of where the key for an
attribute appears during parsing, we do not propagate this
information to the output AST. This means that once we produce the
template AST, we have no way of mapping a template position to the key
span alone. The best we can currently do is map back to the
sourceSpan. This presents problems downstream, specifically for the
language service, where we cannot provide correct information about a
position in a template because the AST is not granular enough.
PR Close#39613
Rather than re-reading component metadata that was already interpreted
by the Ivy compiler, the Language Service should instead use the
compiler APIs to get information it needs about the metadata.
PR Close#39476
This commit takes the `HybridVisitor` in the language service and gives it
the ability to return not just a node but the template context in which it
appears. In the future, more context regarding where a node appears in the
template might become necessary (ex: the microsyntax container for binding
nodes), and this refactoring enables that.
In the process, `HybridVisitor` is renamed and the concept of a
`TemplateTarget` interface is introduced to contain the results of this
operation.
PR Close#39505
This commit refactors the QuickInfo abstraction shared between the VE and
Ivy services and used to implement hover tooltips (quick info), which was
extracted from the VE code in commit faa81dc. The new DisplayParts
abstraction is more general and can be used to extract information needed by
various LS functions (e.g. autocompletion).
This commit effectively reverts faa81dc, returning the original code to the
VE implementation as the Ivy code is now diverged.
PR Close#39505
Currently expressions `$event.foo()` and `this.$event.foo()`, as well as `$any(foo)` and
`this.$any(foo)`, are treated as the same expression by the compiler, because `this` is considered
the same implicit receiver as when the receiver is omitted. This introduces the following issues:
1. Any time something called `$any` is used, it'll be stripped away, leaving only the first parameter.
2. If something called `$event` is used anywhere in a template, it'll be preserved as `$event`,
rather than being rewritten to `ctx.$event`, causing the value to undefined at runtime. This
applies to listener, property and text bindings.
These changes resolve the first issue and part of the second one by preserving anything that
is accessed through `this`, even if it's one of the "special" ones like `$any` or `$event`.
Furthermore, these changes only expose the `$event` global variable inside event listeners,
whereas previously it was available everywhere.
Fixes#30278.
PR Close#39323
Currently render3's `parseTemplate` throws away the parsed AST and
returns an empty list of HTML nodes if HTML->R3 translation failed. This
is not preferrable in some contexts like that of a language service,
where we would like a well-formed AST even if it is has errors.
PR Close#39413
Constructing a project service is expensive. Making it a singleton could
speed up tests considerably.
On my MacBook Pro, test execution went from 24.4s to 14.5s (~40% improvement).
PR Close#39308
Test harness `setup()` is expensive, in the order of ~2.5 seconds.
We could speed up `fit()` tests considerably if `setup()` is wrapped
in `beforeAll()` to avoid running it unnecessarily.
PR Close#39305
This commit enables the Ivy Language Service to 'go to definition' of a
templateUrl or styleUrl, which would jump to the template/style file
itself.
PR Close#39202
These functions will be useful to the Ivy language service as well to
provide 'go to definition' functionality for template and style urls.
PR Close#39202
When an input name is part of the directive selector, it would be good to return the directive as well
when performing 'go to definition' or 'go to type definition'. As an example, this would allow
'go to type definition' for ngIf to take the user to the NgIf directive.
'Go to type definition' would otherwise return no results because the
input is a generic type. This would also be the case for all primitive
input types.
PR Close#39243
This commit fixes a bug in which a new Ivy Compiler is created every time
language service receives a new request. This is not needed if the
`ts.Program` has not changed.
A new class `CompilerFactory` is created to manage Compiler lifecycle and
keep track of template changes so that it knows when to override them.
With this change, we no longer need the method `getModifiedResourceFile()`
on the adapter. Instead, we call `overrideComponentTemplate` on the
template type checker.
This commit also changes the incremental build strategy from
`PatchedIncrementalBuildStrategy` to `TrackedIncrementalBuildStrategy`.
PR Close#39231
For directives/components, it would be generally more appropriate for
"go to type definition" to be the function which navigates to the class
definition. However, for a better user experience, we should do this
for "go to definition" as well.
PR Close#39228
Previously the `ConcreteDeclaration` and `InlineDeclaration` had
different properties for the underlying node type. And the `InlineDeclaration`
did not store a value that represented its declaration.
It turns out that a natural declaration node for an inline type is the
expression. For example in UMD/CommonJS this would be the `exports.<name>`
property access node.
So this expression is now used for the `node` of `InlineDeclaration` types
and the `expression` property is dropped.
To support this the codebase has been refactored to use a new `DeclarationNode`
type which is a union of `ts.Declaration|ts.Expression` instead of `ts.Declaration`
throughout.
PR Close#38959
This PR enables `getSemanticDiagnostics()` to be called on external templates.
Several changes are needed to land this feature:
1. The adapter needs to implement two additional methods:
a. `readResource()`
Load the template from snapshot instead of reading from disk
b. `getModifiedResourceFiles()`
Inform the compiler that external templates have changed so that the
loader could invalidate its internal cache.
2. Create `ScriptInfo` for external templates in MockHost.
Prior to this, MockHost only track changes in TypeScript files. Now it
needs to create `ScriptInfo` for external templates as well.
For (1), in order to make sure we don't reload the template if it hasn't
changed, we need to keep track of its version. Since the complexity has
increased, the adapter is refactored into its own class.
PR Close#39065
Fixed a boolean logic error that prevented hybrid visitor from returning
undefined when a variable does not have value and cursor is not in the
key span.
PR Close#39061
Rather than having the Ivy implementation add the VE code to the deps
list, create a new common package that both Ivy and VE depend on. This
will make it more straightforward in the future to remove the VE code
completely.
PR Close#39098
It used to be the case that all microsyntax bindings share the same source
span, so the second bound attribute would overwrite the first.
This has been fixed in #39036, this case is added to prevent regression.
PR Close#39062
Create stubs for getTypeDefinitionAtPosition for both VE and Ivy Language Service implementations.
This will prevent failed requests when it is implemented on the vscode plugin side
PR Close#39050
In preparation for the Ivy Language service, add the same properties to AppComponent that appear in
TemplateReference so the inline template can be tested thoroughly.
PR Close#39033
Instead of doing all sorts of checks in the `visit()` method, move checks
that are specific to `BoundEvent` to the `visitBoundEvent()` method.
PR Close#38985
Make typing of DatePipe stricter to catch some misuses (such as passing
an Observable or an array) at compile time.
BREAKING CHANGE:
The signature of the `date` pipe now explicitly states which types are
accepted. This should only cause issues in corner cases, as any other
values would result in runtime exceptions.
PR Close#37447
`AsyncPipe.transform` will never return `undefined`, even when passed
`undefined` in input, in contrast with what was declared in the
overloads.
Additionally the "actual" method signature can be updated to match the
most generic case, since the implementation does not rely on wrappers
anymore.
BREAKING CHANGE:
The async pipe no longer claims to return `undefined` for an input that
was typed as `undefined`. Note that the code actually returned `null` on
`undefined` inputs. In the unlikely case you were relying on this,
please fix the typing of the consumers of the pipe output.
PR Close#37447
For the following example, the cursor is between `keySpan` and `valueSpan`
of the `BoundAttribute`.
```html
<test-cmp [foo]¦="bar"></test-cmp>
```
Our hybrid visitor will return `Element`in this case, which is the parent
node of the `BoundAttribute`.
This is because we only look at the `keySpan` and `valueSpan`, and not
the source span. The last element in the AST path is `Element`, so it gets
returned.
In this PR, I propose fixing this by adding a sentinel value `undefined`
to the AST path to signal that we've found a source span but the cursor is
neither in the key span nor the value span.
PR Close#38995
The keySpan in bound attributes provides more fine-grained location information and can be used
to disambiguate multiple bound attributes in a single microsyntax binding. Previously,
this case could not distinguish between the two different attributes because
the sourceSpans were identical and valueSpans would not match if the cursor
was located in a key.
PR Close#38955
In a microsyntax expressions, some attributes are not bound after
desugaring. For example,
```html
<div *ngFor="let item of items">
</div>
```
gets desugared to
```html
<ng-template ngFor let-items [ngForOf]="items">
</ngtemplate>
```
In this case, `ngFor` should be a literal attribute with no RHS value.
Therefore, its source span should be just the `keySpan` and not the
source span of the original template node.
This allows language service to precisely pinpoint different spans in a
microsyntax to provide accurate information.
PR Close#38766
The statements generated in the TCB are optimized for performance and producing diagnostics.
These optimizations can result in generating a TCB that does not have all the information
needed by the `TemplateTypeChecker` for retrieving `Symbol`s. For example, as an optimization,
the TCB will not generate variable declaration statements for directives that have no
references, inputs, or outputs. However, the `TemplateTypeChecker` always needs these
statements to be present in order to provide `ts.Symbol`s and `ts.Type`s for the directives.
This commit adds logic to the TCB generation to ensure the required
information is available in a form that the `TemplateTypeChecker` can
consume. It also adds an option to the `NgCompiler` that makes this
generation configurable.
PR Close#38618
In the test project there are no longer reference markers and location
markers, so there's no need to "pre-process" the source files to remove
them. This will make the Ivy tests cleaner and faster.
PR Close#38777
Previously this interface was mostly stored in compiler-cli, but it
contains some properties that would be useful for compiling the
"declare component" prelink code.
This commit moves some of the interface over to the compiler
package so that it can be referenced there without creating a
circular dependency between the compiler and compiler-cli.
PR Close#38594
Previously, the `startSourceSpan` property could be null
but in reality it is always well defined - except for a legacy
case in the old i18n extraction/merging code, where the
typings for source-spans are already being undermined.
Making this property non-null, simplifies code elsewhere
in the project.
PR Close#38581
In many testing scenarios, there is a common pattern:
1. Overwrite template (inline or external)
2. Find cursor position
3. Call one of language service APIs
4. Inspect spans in result
In order to faciliate this pattern, this commit refactors
`MockHost.overwrite()` and `MockHost.overwriteInlineTemplate()` to
allow a faux cursor symbol `¦` to be injected into the template, and
the methods will automatically remove it before updating the script snapshot.
Both methods will return the cursor position and the new text without
the cursor symbol.
This makes testing very convenient. Here's a typical example:
```ts
const {position, text} = mockHost.overwrite('template.html', `{{ ti¦tle }}`);
const quickInfo = ngLS.getQuickInfoAtPosition('template.html', position);
const {start, length} = quickInfo!.textSpan;
expect(text.substring(start, start + length)).toBe('title');
```
PR Close#38552
This commit introduces two visitors, one for Template AST and the other
for Expression AST to allow us to easily find the node that most closely
corresponds to a given cursor position.
This is crucial because many language service APIs take in a `position`
parameter, and the information returned depends on how well we can find
a good candidate node.
In View Engine implementation of language service, the search for the node
and the processing of information to return the result are strongly coupled.
This makes the code hard to understand and hard to debug because the stack
trace is often littered with layers of visitor calls.
With this new feature, we could test the "searching" part separately and
colocate all the logic (aka hacks) that's required to retrieve an accurate
span for a given node.
Right now, only the most "narrow" node is returned by the main exported
function `findNodeAtPosition`. If needed, we could expose the entire AST
path, or expose other methods to provide more context for a node.
Note that due to limitations in the template AST interface, there are
a few known cases where microsyntax spans are not recorded properly.
This will be dealt with in a follow-up PR.
PR Close#38540
Prior to this change, the unary + and - operators would be parsed as `x - 0`
and `0 - x` respectively. The runtime semantics of these expressions are
equivalent, however they may introduce inaccurate template type checking
errors as the literal type is lost, for example:
```ts
@Component({
template: `<button [disabled]="isAdjacent(-1)"></button>`
})
export class Example {
isAdjacent(direction: -1 | 1): boolean { return false; }
}
```
would incorrectly report a type-check error:
> error TS2345: Argument of type 'number' is not assignable to parameter
of type '-1 | 1'.
Additionally, the translated expression for the unary + operator would be
considered as arithmetic expression with an incompatible left-hand side:
> error TS2362: The left-hand side of an arithmetic operation must be of
type 'any', 'number', 'bigint' or an enum type.
To resolve this issues, the implicit transformation should be avoided.
This commit adds a new unary AST node to represent these expressions,
allowing for more accurate type-checking.
Fixes#20845Fixes#36178
PR Close#37918
Now that Ivy compiler has a proper `TemplateTypeChecker` interface
(see https://github.com/angular/angular/pull/38105) we no longer need to
keep the temporary compiler implementation.
The temporary compiler was created to enable testing infrastructure to
be developed for the Ivy language service.
This commit removes the whole `ivy/compiler` directory and moves two
functions `createTypeCheckingProgramStrategy` and
`getOrCreateTypeCheckScriptInfo` to the `LanguageService` class.
Also re-enable the Ivy LS test since it's no longer blocking development.
PR Close#38310
This commit removes compiler instantiation at startup.
This is because the constructor is invoked during the plugin loading phase,
in which the project has not been completely loaded.
Retrieving `ts.Program` at startup will trigger an `updateGraph` operation,
which could only be called after the Project has loaded completely.
Without this change, the Ivy LS cannot be loaded as a tsserver plugin.
Note that the whole `Compiler` class is temporary, so changes made there are
only for development. Once we have proper integration with ngtsc the
`Compiler` class would be removed.
PR Close#38120
The template type-checking engine relies on the abstraction interface
`TypeCheckingProgramStrategy` to create updated `ts.Program`s for
template type-checking. The basic API is that the type-checking engine
requests changes to certain files in the program, and the strategy provides
an updated `ts.Program`.
Typically, such changes are made to 'ngtypecheck' shim files, but certain
conditions can cause template type-checking to require "inline" operations,
which change user .ts files instead. The strategy used by 'ngc' (the
`ReusedProgramStrategy`) supports these kinds of updates, but other clients
such as the language service might not always support modifying user files.
To accommodate this, the `TypeCheckingProgramStrategy` interface was
modified to include a `supportsInlineOperations` flag. If an implementation
specifies `false` for inline support, the template type-checking system will
return diagnostics on components which would otherwise require inline
operations.
Closes#38059
PR Close#38105
This commit significantly refactors the 'typecheck' package to introduce a
new abstraction, the `TemplateTypeChecker`. To achieve this:
* a 'typecheck:api' package is introduced, containing common interfaces that
consumers of the template type-checking infrastructure can depend on
without incurring a dependency on the template type-checking machinery as
a whole.
* interfaces for `TemplateTypeChecker` and `TypeCheckContext` are introduced
which contain the abstract operations supported by the implementation
classes `TemplateTypeCheckerImpl` and `TypeCheckContextImpl` respectively.
* the `TemplateTypeChecker` interface supports diagnostics on a whole
program basis to start with, but the implementation is purposefully
designed to support incremental diagnostics at a per-file or per-component
level.
* `TemplateTypeChecker` supports direct access to the type check block of a
component.
* the testing utility is refactored to be a lot more useful, and new tests
are added for the new abstraction.
PR Close#38105
Previously in the template type-checking engine, it was assumed that every
input file would have an associated type-checking shim. The type check block
code for all components in the input file would be generated into this shim.
This is fine for whole-program type checking operations, but to support the
language service's requirements for low latency, it would be ideal to be
able to check a single component in isolation, especially if the component
is declared along with many others in a single file.
This commit removes the assumption that the file/shim mapping is 1:1, and
introduces the concept of component-to-shim mapping. Any
`TypeCheckingProgramStrategy` must provide such a mapping.
To achieve this:
* type checking record information is now split into file-level data as
well as per-shim data.
* components are now assigned a stable `TemplateId` which is unique to the
file in which they're declared.
PR Close#38105
This commit adds a script to build @angular/language-service
locally so that it can be consumed by the Angular extension for
local development.
PR Close#38103
Currently the Ivy language service bundle is [10MB](
https://unpkg.com/browse/@angular/language-service@10.0.4/bundles/) because we
accidentally included typescript in the bundle.
With this change, the bundle size goes down to 1.6MB, which is even smaller
than the View Engine bundle (1.8MB).
```bash
$ yarn bazel build //packages/language-service/bundles:ivy
$ ls -lh dist/bin/packages/language-service/bundles/ivy.umd.js
1.6M Jul 15 15:49 dist/bin/packages/language-service/bundles/ivy.umd.js
```
PR Close#38088
`ls_rollup_bundle` is no longer needed since we could invoke `ng_rollup_bundle`
directly.
Background: language service runs rollup to produce a single file to reduce
startup time in the editor. However, due to the need to load dynamic versions
of typescript at runtime (think the case where users can change typescript
version in their editor), we hack the "banner" to export a CommonJS default function,
so that we could dynamically load the typescript module provided at runtime via AMD
and use it throughout the implementation.
PR Close#38086
This commit fixes a bug whereby the language service would incorrectly
return HTML elements if autocomplete is requested for an unknown symbol.
This is because we walk through every possible scenario, and fallback to
element autocomplete if none of the scenarios match.
The fix here is to return results from interpolation if we know for sure
we are in a bound text. This means we will now return an empty results if
there is no suggestions.
This commit also refactors the code a little to make it easier to
understand.
PR Close#37518
`getExternalFiles()` is an API that could optionally be provided by a tsserver plugin
to notify the server of any additional files that should belong to a particular project.
This API was removed in https://github.com/angular/angular/pull/34260 mainly
due to performance reasons.
However, with the introduction of "solution-style" tsconfig in typescript 3.9,
the Angular extension could no longer reliably detect the owning Project solely
based on the ancestor tsconfig.json. In order to support this use case, we have
to reinstate `getExternalFiles()`.
Fixes https://github.com/angular/vscode-ng-language-service/issues/824
PR Close#37750
The language-service package currently sets the `module` `package.json`
property and refers to a folder called `fesm5`. The language-service
though does not build with `ng_package` so this folder never existed.
Now with APF v10, ng package would not generate this folder either.
We should just remove the property as the primary entry-point is
the UMD bundle resolved through `main`. There is no module flavour
exposed to the NPM package as `pkg_npm` uses the named AMD module
devmode output that doesn't work for `module`.
PR Close#37623
Refactors the `ng_rollup_bundle` rule to a macro that relies on
the `@bazel/rollup` package. This means that the rule no longer
deals with custom ESM5 flavour output, but rather only builds
prodmode ES2015 output. This matches the common build output
in Angular projects, and optimizations done in CLI where
ES2015 is the default optimization input.
The motiviation for this change is:
* Not duplicating rollup Bazel rules. Instead leveraging the official
rollup rule.
* Not dealing with a third TS output flavor in Bazel.The ESM5 flavour has the
potential of slowing down local development (as it requires compilation replaying)
* Updating the rule to be aligned with current CLI optimizations.
This also _fixes_ a bug that surfaced in the old rollup bundle rule.
Code that is unused, is not removed properly. The new rule fixes this by
setting the `toplevel` flag. This instructs terser to remove unused
definitions at top-level. This matches the optimization applied in CLI
projects. Notably the CLI doesn't need this flag, as code is always
wrapped by Webpack. Hence, the unused code eliding runs by default.
PR Close#37623
Commit 24b2f1da2b introduced an `NgCompiler` which operates on a
`ts.Program` independently of the `NgtscProgram`. The NgCompiler got its
`IncrementalDriver` (for incremental reuse of Angular compilation results)
by looking at a monkey-patched property on the `ts.Program`.
This monkey-patching operation causes problems with the Angular indexer
(specifically, it seems to cause the indexer to retain too much of prior
programs, resulting in OOM issues). To work around this, `IncrementalDriver`
reuse is now handled by a dedicated `IncrementalBuildStrategy`. One
implementation of this interface is used by the `NgtscProgram` to perform
the old-style reuse, relying on the previous instance of `NgtscProgram`
instead of monkey-patching. Only for `NgTscPlugin` is the monkey-patching
strategy used, as the plugin sits behind an interface which only provides
access to the `ts.Program`, not a prior instance of the plugin.
PR Close#37339
In `a ? b.~{cursor}`, the LS will provide the symbols in the scope of the current template, because the `path.tail` is `falseExp` whose value is `EmptyExpr`, and the span of `falseExp` is wider than the `trueExp`, so the value of `path` should be narrowed.
PR Close#37505
This commit removes the autocompletion feature for HTML entities.
HTML entites are things like `&`, `<` etc.
There are a few reasons for the decision:
1. It is outside the core functionality of Angular LS
2. The implementation relies on regex, which incurs performance cost
3. There isn't much value if users do not already know which entity
they want to use
4. The list that we provide is not exhaustive
PR Close#37515
Keen and I were talking about what it would take to support getting
references at a position in the current language service, since it's
unclear when more investment in the Ivy LS will be available. Getting TS
references from a template is trivial -- we simply need to get the
definition of a symbol, which is already handled by the language
service, and ask the TS language service to give us the references for
that definition.
This doesn't handle references in templates, but that could be done in a
subsequent pass.
Part of https://github.com/angular/vscode-ng-language-service/issues/29
PR Close#37437
The function signature selection algorithm is totally naive. It'd
unconditionally pick the first signature if there are multiple
overloads. This commit improves the algorithm by returning an exact
match if one exists.
PR Close#37494
This commit removes all markers from the inline template in
`AppComponent` and external template in `TemplateReference`.
Test scenarios should be colocated with the test cases themselves.
Besides, many existing cases are invalid. For example, if we want to
test autocomplete for HTML element, the existing test case is like:
```
<~{cursor} h1>
```
This doesn't make much sense, becasue the language service already sees
the `h1` tag in the template. The correct test case should be:
```
<~{cursor
```
IMO, this reflects the real-world use case better.
This commit also uncovers a bug in the way HTML entities autocompletion
is done. There's an off-by-one error in which a cursor that immediately
trails the ampersand character fails to trigger HTML entities
autocompletion.
PR Close#37475
This commit disables the tests for Ivy version of language service on CI
because the compiler APIs are not yet stable, so language service should
not assert against its behavipr.
PR Close#37348
Due to an outage with the proxy we rely on for publishing, we need
to temporarily directly publish to NPM using our own angular
credentials again.
PR Close#37378
This commit removes the `bootstrap()` function in the test project since
its presence has no effect on the behavior of language service.
Also removes the explicit cast when instantiating `CounterDirectiveContext`,
and let type inference takes care of that.
PR Close#37122
With this change we update the expect of the `module resolution cache` were in the second count with cache the `fileExists` is called less then 700 times.
PR Close#36989
In TypeScript 3.9, the compiler is able to re-use (i.e. not invalidate)
the previous program if only external templates (i.e. no TS files) have
changed.
PR Close#36989
In TypeScript 3.9, type nodes need to exist in the context of a statement.
This commit ensures that the synthetic type node has such a parent.
PR Close#36989
This commit refactors TS-only utility functions to a separate file so
that they could be shared with Ivy language service.
A separate ts_library rule is created so that there is no dependency on
`compiler` and `compiler-cli` to make the compilation fast and
light-weight.
The method `getPropertyAssignmentFromValue` is modified slightly to
improve the ergonomics of the function.
PR Close#36984
ASTs for property read and method calls contain information about
the entire span of the expression, including its receiver. Use cases
like a language service and compile error messages may be more
interested in the span of the direct identifier for which the
expression is constructed (i.e. an accessed property). To support this,
this commit adds a `nameSpan` property on
- `PropertyRead`s
- `SafePropertyRead`s
- `PropertyWrite`s
- `MethodCall`s
- `SafeMethodCall`s
The `nameSpan` property already existed for `BindingPipe`s.
This commit also updates usages of these expressions' `sourceSpan`s in
Ngtsc and the langauge service to use `nameSpan`s where appropriate.
PR Close#36826
This commit adds a Compiler interface that wraps the actual ngtsc
compiler. The language-service specific compiler manages multiple
typecheck files using the Project interface, creating and adding
ScriptInfos as necessary.
This commit also adds `overrideInlineTemplate()` method to the mock
service so that we could test the Compiler diagnostics feature.
PR Close#36930
Add a mechanism to replace file contents for a specific file. This
allows us to write custom test scenarios in code without modifying the
test project.
Since we are no longer mocking the language service host, the file
overwrite needs to happen via the project service.
Project service manages a set of script infos, and overwriting the files
is a matter of updating the relevant script infos.
Note that the actual project service is wrapped inside a Mock Service.
Tests should not have direct access to the project service. All
manipulations should take place via the Mock Service.
The MockService provides a `reset()` method to undo temporary overwrites
after each test.
PR Close#36923
Parse Angular compiler options in Angular language service.
In View Engine, only TypeScript compiler options are read, Angular
compiler options are not. With Ivy, there could be different modes of
compilation, most notably how strict the templates should be checked.
This commit makes the behavior of language service consistent with the
Ivy compiler.
PR Close#36922
This commit adds a method `overrideInlineTemplate` to the
`MockTypescriptHost`. This allows us to override an inline template
in a Component without changing the TypeScript parts. This methods works
in a similar way as `MockTypescriptHost.override()`, which is used for
overriding external template.
PR Close#36890
This commit adds a new mock host for testing the ivy language service.
Unlike the existing mock_host which mocks the LanguageServiceHost, the
Ivy mock host mocks just the filesystem interface, aka ts.ServerHost.
This is because Ivy language service requires an actual Project to
perform operations like adding synthetic typecheck files to the project,
and by extension, to the ts.Program. These requirements make the existing
mock host unsuitable to be reused.
This new testing structure also improves test performance, because the
old mock host copies (it actually creates symlinks, but still that's
relatively expensive due to the sheer number of files involved) all
@angular/* packages along with the typescript package to a temporary
node_modules directory. This is done every time setup() is called.
Instead, this new mock host just loads them from a pre-determined path
in Bazel runfiles.
PR Close#36879
This commit makes the test project a filegroup so that it could be
shared with the Ivy tests.
Also removed `project/foo.ts` since it is no longer used.
PR Close#36865
This commit adds a new entry point for the Ivy version of language
service. The entry point is just a shell for now, implementation will be
added in subsequent PRs.
The Ivy version of language service could be loaded from the NPM package
via `require(@angular/language-service/bundles/ivy.umd.js)`
PR Close#36864
This property can actually be `null` when called from the language-service.
This change allows us to remove the use of `!` to subvert the type system.
PR Close#36741
After the user edits the file `core.d.ts`, the symbol from the core module will be invalided, which only is created when init the language service. Then the language-service will crash.
PR Close#36783
* Move tools/brotli-cli, tools/browsers, tools/components,
tools/ng_rollup_bundle, and modules/e2e_util to dev-infra/benchmarking
* Fix imports and references to moved folders and files
* Set up BUILD.bazel files for moved folders so they can be packaged with
dev-infra's :npm_package
PR Close#36434
This commit fixes how the language service evaluates the compatibility
of types to work with arbitrary union types. As a result, compatibility
checks are now more strict and can catch similarities or differences
more clearly.
```
number|string == string|null // OK
number|string == number // OK
number|string == null // not comparable
number == string // not comparable
```
Using Ivy as a backend should provide these diagnoses for free, but we
can backfill them for now.
Closes https://github.com/angular/vscode-ng-language-service/issues/723
PR Close#36529
This commit removes individual components from parsing-cases.ts and
colocate them with the actual tests. This makes the tests more readable.
PR Close#36495
1. update jasmine to 3.5
2. update @types/jasmine to 3.5
3. update @types/jasminewd2 to 2.0.8
Also fix several cases, the new jasmine 3 will help to create test cases correctly,
such as in the `jasmine 2.x` version, the following case will pass
```
expect(1 == 2);
```
But in jsamine 3, the case will need to be
```
expect(1 == 2).toBeTrue();
```
PR Close#34625
This commit refactors the process for determining the type of an Angular
attribute to be use a function that takes an attribute name and returns
the Angular attribute kind and name, rather than requiring the user to
query match the attribute name with the regex and query the matching
array.
This refactor prepares for a future change that will improve the
experience of completing attributes in `()`, `[]`, or `[()]` contexts.
PR Close#36301
clang-format was recently updated and any PRs that touch files in the
language service will have to reformat all the files.
Instead of changing the formatting in those PRs, this PR formats all
files in language-service package once and for all.
PR Close#36426
Currently the language service only provides support for determining the
type of array-like members when the array-like object is an `Array`.
However, there are other kinds of array-like objects, including
`ReadonlyArray`s and `readonly`-property arrays. This commit adds
support for retrieving the element type of arbitrary array-like objects.
Closes#36191
PR Close#36312
This commit improves the context of a non-callable function error
message by providing the affected call target and its non-callable type.
PR Close#35271
This commit propagates the `sourceSpan` and `valueSpan` of a `VariableBinding`
in a microsyntax expression to `ParsedVariable`, and subsequently to
View Engine Variable AST and Ivy Variable AST.
Note that this commit does not propagate the `keySpan`, because it involves
significant changes to the template AST.
PR Close#36047
Today, the language service infers the type of variables bound to the
"ngIf" template context member of an NgIf directive, but does not do the
same for the the "$implicit" context member. This commit adds support
for that.
Fixes https://github.com/angular/vscode-ng-language-service/issues/676
PR Close#35941
This commit adds fine-grained text spans to TemplateBinding for microsyntax expressions.
1. Source span
By convention, source span refers to the entire span of the binding,
including its key and value.
2. Key span
Span of the binding key, without any whitespace or keywords like `let`
The value span is captured by the value expression AST.
This is part of a series of PRs to fix source span mapping in microsyntax expression.
For more info, see the doc https://docs.google.com/document/d/1mEVF2pSSMSnOloqOPQTYNiAJO0XQxA1H0BZyESASOrE/edit?usp=sharing
PR Close#35897
This commit adds support in the Angular monorepo and in the Angular
compiler(s) for TypeScript 3.8. All packages can now compile with
TS 3.8.
For most of the repo, only a handful few typings adjustments were needed:
* TS 3.8 has a new `CustomElementConstructor` DOM type, which enforces a
zero-argument constructor. The `NgElementConstructor` type previously
declared a required `injector` argument despite the fact that its
implementation allowed `injector` to be optional. The interface type was
updated to reflect the optionality of the argument.
* Certain error messages were changed, and expectations in tests were
updated as a result.
* tsserver (part of language server) now returns performance information in
responses, so test expectations were changed to only assert on the actual
body content of responses.
For compiler-cli and schematics (which use the TypeScript AST) a major
breaking change was the introduction of the export form:
```typescript
export * as foo from 'bar';
```
This is a `ts.NamespaceExport`, and the `exportClause` of a
`ts.ExportDeclaration` can now take this type as well as `ts.NamedExports`.
This broke a lot of places where `exportClause` was assumed to be
`ts.NamedExports`.
For the most part these breakages were in cases where it is not necessary
to handle the new `ts.NamedExports` anyway. ngtsc's design uses the
`ts.TypeChecker` APIs to understand syntax and so automatically supports the
new form of exports.
The View Engine compiler on the other hand extracts TS structures into
metadata.json files, and that format was not designed for namespaced
exports. As a result it will take a nontrivial amount of work if we want to
support such exports in View Engine. For now, these new exports are not
accounted for in metadata.json, and so using them in "folded" Angular
expressions will result in errors (probably claiming that the referenced
exported namespace doesn't exist).
Care was taken to only use TS APIs which are present in 3.7/3.6, as Angular
needs to remain compatible with these for the time being.
This commit does not update angular.io.
PR Close#35864
This commit accomplishes two tasks:
- Fixes the span of queried pipes to only be applied on pipe names
- By consequence, fixes how pipes are located in arguments (previously,
pipes with arguments could not be found because the span of a pipe
uses a relative span, while the template position is absolute)
The screenshots attached to the PR for this commit demonstrate the
change.
Closes https://github.com/angular/vscode-ng-language-service/issues/677
PR Close#35986
The test libs should only be included in one jasmine_node_test
otherwise `bazel build //packages/language-service/...` would
end up running `feature_test` and `infra_test` twice.
PR Close#35816
This commit performs a modularization of the Language Service's existing
diagnostic messages. Such a modularization has two primary advantages:
- Centralization and decoupling of error messages from the code that
generates them makes it easy to add/delete/edit diagnostic messages,
and allows for independent iteration of diagnostic messages and
diagnostic generation.
- Prepares for additional features like annotating the locations where a
diagnostic is generated and enabling the configuration of which
diagnostics should be reported by the language service.
Although it would be preferable to place the diagnostics registry in an
independent JSON file, for ease of typing diagnostic types as an enum
variant of 'ts.DiagnosticCategory', the registry is stored as an object.
Part of #32663.
PR Close#35678
This commit removes the `NullAstVisitor` and `visitAstChildren` exported
from `packages/compiler/src/expression_parser/ast.ts` because they
contain duplicate and buggy implementation, and their use cases could be
sufficiently covered by `RecursiveAstVisitor` if the latter implements the
`visit` method. This use case is only needed in the language service.
With this change, any visitor that extends `RecursiveAstVisitor` could
just define their own `visit` function and the parent class will behave
correctly.
A bit of historical context:
In language service, we need a way to tranverse the expression AST in a
selective manner based on where the user's cursor is. This means we need a
"filtering" function to decide which node to visit and which node to not
visit. Instead of refactoring `RecursiveAstVisitor` to support this,
`visitAstChildren` was created. `visitAstChildren` duplicates the
implementation of `RecursiveAstVisitor`, but introduced some bugs along
the way. For example, in `visitKeyedWrite`, it visits
```
obj -> key -> obj
```
instead of
```
obj -> key -> value
```
Moreover, because of the following line
```
visitor.visit && visitor.visit(ast, context) || ast.visit(visitor, context);
```
`visitAstChildren` visits every node *twice*.
PR Close#35619
This commit differentiates language service feature and language service
infrastructure tests. This is primarily to make testing of different
components at the development level easier. This commit continues a
small effort to expand our test coverage and normalize testing
structure.
Also adds test coverage to language service tests. We have quite a bit
to go on that front 🙂.
PR Close#35688
This commit normalizes hover and util tests in the language service.
This is part of a small effort to simplify and normalize the language
service testing structure, which currently contains specs that are
largely created and left without relation to other tests.
PR Close#35656
For example, '<div><p string-model~{cursor}></p></div>', when provide the hover info for 'string-model', the 'path.head' is root tag 'div'. Use the parent of 'path.tail' instead.
PR Close#35317
* it's tricky to get out of the runfiles tree with `bazel test` as `BUILD_WORKSPACE_DIRECTORY` is not set but I employed a trick to read the `DO_NOT_BUILD_HERE` file that is one level up from `execroot` and that contains the workspace directory. This is experimental and if `bazel test //:test.debug` fails than `bazel run` is still guaranteed to work as `BUILD_WORKSPACE_DIRECTORY` will be set in that context
* test //integration:bazel_test and //integration:bazel-schematics_test exclusively
* run "exclusive" and "manual" bazel-in-bazel integration tests in their own CI job as they take 8m+ to execute
```
//integration:bazel-schematics_test PASSED in 317.2s
//integration:bazel_test PASSED in 167.8s
```
* Skip all integration tests that are now handled by angular_integration_test except the tests that are tracked for payload size; these are:
- cli-hello-world*
- hello_world__closure
* add & pin @babel deps as newer versions of babel break //packages/localize/src/tools/test:test
@babel/core dep had to be pinned to 7.6.4 or else //packages/localize/src/tools/test:test failed. Also //packages/localize uses @babel/generator, @babel/template, @babel/traverse & @babel/types so these deps were added to package.json as they were not being hoisted anymore from @babel/core transitive.
NB: integration/hello_world__systemjs_umd test must run with systemjs 0.20.0
NB: systemjs must be at 0.18.10 for legacy saucelabs job to pass
NB: With Bazel 2.0, the glob for the files to test `"integration/bazel/**"` is empty if integation/bazel is in .bazelignore. This glob worked under these conditions with 1.1.0. I did not bother testing with 1.2.x as not having integration/bazel in .bazelignore is correct.
PR Close#33927
I think the bug is introduced in my PR#34847. For example, 'model="{{title}}"', the attribute value cannot be parsed by 'parseTemplateBindings'.
PR Close#35494
The completions list don't include the 'ngTemplateOutlet'. The directive is a structural directive when it has injected the 'TemplateRef' or 'ViewContainerRef'.
PR Close#35466
Under strict mode, the language service fails to typecheck nullable
symbols that have already been verified to be non-null.
This generates incorrect (false positive) and confusing diagnostics
for users.
To work around this issue in the short term, this commit changes the
diagnostic message from an error to a suggestion, and prompts users to
use the safe navigation operator (?) or non-null assertion operator (!).
For example, instead of
```typescript
{{ optional && optional.toString() }}
```
the following is cleaner:
```typescript
{{ optional?.toString() }}
{{ optional!.toString() }}
```
Note that with this change, users who legitimately make a typo in their
code will no longer see an error. I think this is acceptable, since
false positive is worse than false negative. However, if users follow
the suggestion, add ? or ! to their code, then the error will be surfaced.
This seems a reasonable trade-off.
References:
1. Safe navigation operator (?)
https://angular.io/guide/template-syntax#the-safe-navigation-operator----and-null-property-paths
2. Non-null assertion operator (!)
https://angular.io/guide/template-syntax#the-non-null-assertion-operator---
PR closes https://github.com/angular/angular/pull/35070
PR closes https://github.com/angular/vscode-ng-language-service/issues/589
PR Close#35200