We keep getting PRs that target single usages of `hasOwnProperty` and we have ~100 of them. These changes aim to address the issue centrally by swapping out all the instances and adding a lint rule against introducing new ones.
(cherry picked from commit 732e505018)
When a view has more than one effect scheduled to run, and one of them
destroys the view (e.g. by calling `componentRef.destroy()`), the
remaining effects in that same flush could still run afterward,
against a view that no longer exists. In some cases this crashed
outright with `TypeError: view[EFFECTS] is not iterable`.
Here's why: `runEffectsInView` walks a view's effects in a `for...of`
loop, wrapped in an outer `while` loop that re-checks for any effects
that became dirty as a side effect of ones that already ran. When an
effect destroys its view, `view[EFFECTS]` gets set to `null` as part
of tearing the view down.
First attempt checked for that inside the `for...of` loop, before each
effect runs. That covers a sibling effect later in the *same* pass,
but misses a second case: if the effect that destroys the view *also*
dirties another effect on that same view in the process (e.g. by
writing a signal the sibling depends on), the outer `while` loop sees
`HasChildViewsToRefresh` set and tries to restart — and immediately
crashes re-entering `for (const effect of view[EFFECTS])` on a
now-null value, before the in-loop check ever gets a chance to run.
Reproduced that exact crash with a test first: two effects on one
view, the second one writes a signal the first depends on and then
destroys the view in the same call — confirmed it throws before
touching the fix.
Fixed by checking right after `effect.run()` instead of before it,
covering both cases in one place: the remaining effects in the current
pass, and the loop trying to restart afterward. As soon as one effect
destroys the view, nothing else runs against it again.
This is intentionally narrow in scope. An earlier version of this fix
also tried to guarantee that `onCleanup()` callbacks still ran even
when registered after an effect destroyed its own view. That's been
dropped — destroying your own view and then continuing to register
more work for it isn't something the framework should have to paper
over. If you need to do both, register `onCleanup` first, then
destroy.
(cherry picked from commit c658d73210)
Previously, when you passed an object typed like
Record<'a' | 'b', number> into the `keyvalue` pipe, TypeScript would
"forget" that the keys could only ever be 'a' or 'b', and just tell
you the key was a plain `string` instead. So code like this used to
fail to compile, even though it's correct:
```ts
const input: Record<'a' | 'b', number> = {a: 1, b: 2};
const result = pipe.transform(input);
const key: 'a' | 'b' = result[0].key; // error: string is not 'a' | 'b'
```
This happened because the pipe has multiple overloaded versions of
transform(), and TypeScript checks them top to bottom, using the
first one that matches. The "number keys" overload was listed first,
and it happened to also match string-keyed objects by accident, so
it "won" before the correct "string keys" overload ever got a
chance to run.
The fix just reorders those two overloads so the string-keys one is
checked first. Nothing about runtime behavior changes — objects with
actual numeric keys (e.g. Record<1 | 2, string>) still correctly
report their keys as plain `string`, matching what Object.keys()
really returns at runtime.
(cherry picked from commit 46d2cb7ff0)
Prevent arbitrary MetaDefinition properties from writing on* handlers directly to meta elements. Browser events can execute these handlers, including on meta elements rendered in the document body.
(cherry picked from commit 6f9a6bea50)
In a composite/solution-style workspace (e.g. an Nx monorepo, where an
app's tsconfig.json only contains project references), TypeScript can
never resolve a config file for an HTML file, since HTML files are not
listed in any referenced project. angular/vscode-ng-language-service#2165
worked around this in onDidOpenTextDocument by briefly opening the
sibling TS file so the right project loads when a template is opened
first.
However, getDefaultProjectForScriptInfo - the recovery path used by
getLSAndScriptInfo and onDidChangeTextDocument when a script info has no
configured project - did not receive the same workaround. When an open
template loses its project association (e.g. its component file is
closed and the project graph updates), every subsequent request on the
template fails with "No config file" and returns null indefinitely,
until the user manually reopens the component file.
Apply the same sibling-TS best effort in getDefaultProjectForScriptInfo,
and additionally attach the template's script info to the configured
project of its component when the config lookup still comes back empty
(openClientFile does not repeat the config lookup for already-open
files).
Also skip the sibling lookup when the .ts file does not exist, so
non-component HTML files (e.g. src/index.html) do not trigger an
open/close and config search that cannot succeed.
Fixes#69768
(cherry picked from commit 3f8d9d6ea6)
The "Value transformation" section taught readers to hand-roll
transformation with `linkedSignal()` and a manual parse method, even
though `@angular/forms/signals` ships `transformedValue()` for exactly
this case. Readers ended up with a weaker version of a feature the
framework already provides — notably, no parse error reporting.
Rewrite the section around `transformedValue()` and document the parts
the manual pattern could not cover: returning `{error}` from `parse` to
surface parse errors on the field's `errors()`, and `reset()` clearing
them. This also makes good on the cross-reference from the validation
guide, which pointed here for parse error details the section never
covered.
Fixes#70206
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
(cherry picked from commit 182c371d82)
Replace unsafe `any` type annotations with `unknown` across the localize
utility layer to improve type safety and catch potential type errors at
compile time rather than at runtime.
Changes in `messages.ts`:
- `ParsedMessage.substitutions`: `Record<string, any>` → `Record<string, unknown>`
- `parseMessage` parameter `expressions`: `readonly any[]` → `readonly unknown[]`
- Local `substitutions` variable: `{[key: string]: any}` → `Record<string, unknown>`
Changes in `translations.ts`:
- `isMissingTranslationError` parameter: `any` → `unknown`, with proper
narrowing (`typeof e === 'object' && e !== null`) before property access
- `MissingTranslationError.type` visibility: `private` → `readonly` to allow
access through the narrowed `unknown` type in the type guard
- `translate` parameter and return type: `readonly any[]` → `readonly unknown[]`
- `makeTemplateObject` cast: `cooked as any` → `cooked as unknown as TemplateStringsArray`
Fix in `mock_message.ts` (test helper):
- `substitutions: []` → `substitutions: {}` — the array literal was only
assignable because the field was typed as `any`; the correct empty value
for a `Record<string, unknown>` is an object literal
(cherry picked from commit b10021c79b)
NgModel injects ControlContainer with @Host(), which stops the injector at
the component host element boundary. When NgForm lives in a parent component
and ngModel lives in a child component, the injection returns null silently
and the control acts standalone — never registering with the form.
To surface this invisible failure, emit a dev-mode warning (NG01354) when
ngModel's @Host() injection finds nothing but the element Injector can still
reach a ControlContainer further up the hierarchy. The warning identifies the
cross-boundary issue and points developers to the viewProviders fix or the
standalone option.
Adds the NG01354 reference page explaining why the warning fires and providing
two remediation paths: bridging ControlContainer via viewProviders, or opting
out with [ngModelOptions]="{standalone: true}".
Fixes#47580
(cherry picked from commit 38d093232c)
This test makes sure that when Angular is served from Electron or just from the file system that the router works correctly.
(cherry picked from commit 70b252cac3)
StateValue and AnimationTransitionNamespace.trigger detect the {value,
params} object form of a trigger binding by calling hasOwnProperty on the
bound value. When that value is an object from untrusted data (for example
a parsed JSON payload) carrying an own hasOwnProperty key, the shadowed
property is called as a method and throws, breaking the animation flush.
Use Object.hasOwn for the check so a shadowing key no longer matters.
(cherry picked from commit c73a001fbf)
Cancel the unread response body before reporting NG02825 when its declared Content-Length exceeds the configured buffer limit. Without cancellation, SSR can finish while the underlying connection remains open.
Add regression coverage for the declared-length rejection path.
(cherry picked from commit 1a006a8f97)
In v22, `strictTemplates` is true by default. We need to adapte the LS to online report the suggestion when the option is explicitly `false`
(cherry picked from commit ed3373868d)
The routing header draws a serpentine route with map pins on it but
nobody travelling it. Angie now stands on the top segment, the same way
she stands on the road in the roadmap header.
She needs more headroom than the canvas had, so the viewBox gains 12
units at the top and the illustration carries its own max-height, which
keeps the rest of the drawing at the size it rendered before.
(cherry picked from commit 064530447e)
locateOrCreateElementNodeImpl looks up the DOM node for an element
during hydration and immediately checks its nodeType. If the
client-rendered DOM has fewer nodes than the server-rendered HTML,
the lookup returns null, and in production that null flows straight
into the nodeType check and crashes with a raw, uncoded
"Cannot read properties of null (reading 'nodeType')" TypeError.
The dev-mode check that would normally catch this (validateMatchingNode)
already handles a missing node, but it's compiled out of production
builds, so the crash only shows up outside of dev mode.
Add a null check ahead of the nodeType check that throws a coded
RuntimeError using the existing HYDRATION_MISSING_NODE (NG0502) code,
with a descriptive message in dev mode and a cheap fallback in
production. Also add a regression test that removes a server-rendered
element before hydration runs and asserts a coded RuntimeError is
thrown instead of a raw TypeError.
(cherry picked from commit c6e4a36be1)
Angular's internal LView/TNode bookkeeping can get out of sync with the
real DOM: manual DOM manipulation, a browser extension, or an edge case in
Angular's own view-insertion/reordering code can all leave Angular believing
a node is still attached at a given position when it isn't. The next time
Angular's renderer calls `insertBefore` relative to that stale reference
node, the native DOM API throws an opaque `NotFoundError` with no indication
of which node or component was involved, making these errors effectively
undebuggable in production:
NotFoundError: Failed to execute 'insertBefore' on 'Node': The node
before which the new node is to be inserted is not a child of this node.
at Node.insertBefore (native)
at DefaultDomRenderer2.insertBefore (packages/platform-browser/src/dom/dom_renderer.ts)
at nativeInsertBefore (packages/core/src/render3/dom_node_manipulation.ts)
at nativeAppendOrInsertBefore (packages/core/src/render3/dom_node_manipulation.ts)
... (called while Angular inserts or moves a view during change detection)
Check the reference node's actual parent against the expected parent before
calling the native `insertBefore`, and throw a descriptive `RuntimeError`
(NG05106) instead, following the same pattern already used for hydration
node mismatches.
(cherry picked from commit 1cb3d606bf)
VS Code parses notification text for links and permits `command:` URIs. Interpolating the workspace-controlled TSDK path can therefore let a malicious path add a command link to the approval prompt.
Remove the path from the notification instead of attempting to sanitize or escape it. This keeps the prompt static and matches VS Code's TypeScript extension workspace-version approval flow.
Fixes#70176
(cherry picked from commit ba13a3ce22)
When reflection metadata is emitted via setClassMetadata, passing readonly arrays or const tuples for the decorators parameter causes TypeScript type checking errors because setClassMetadata previously expected decorators to be a mutable any[] or null.
This change updates the setClassMetadata type signature to accept decorators as readonly any[] or null and casts the parameter internally when mutating the class metadata property.
(cherry picked from commit dc65e3656f)
When components pass arrays or readonly tuples to decorator metadata fields (such as ), typechecking generated decorator reflection metadata causes a TS2322 type mismatch error because previously only accepted mutable .
This change updates to accept , allowing arrays and readonly tuples to be assigned without TypeScript compilation errors.
(cherry picked from commit eee9ef4d09)
When hydration locates the DOM node for an ɵɵelementStart/ɵɵdomElementStart
instruction, locateOrCreateElementNodeImpl assumed the located node was
always an Element and called hasSkipHydrationAttrOnRElement(native), which
does native.hasAttribute(...). The check that would normally catch this
class of mismatch, validateMatchingNode, is gated behind `ngDevMode &&` and
is compiled out of production builds. So when a real SSR/hydration
structural mismatch located a Text or Comment node instead of the expected
Element, production builds hit .hasAttribute on a node type that doesn't
have it and crashed with a raw, uncoded TypeError instead of a coded
hydration-mismatch RuntimeError.
Add a cheap, always-on nodeType check ahead of that call. On mismatch it
throws RuntimeError(HYDRATION_NODE_MISMATCH, ngDevMode && '...'), the same
pattern used elsewhere in the codebase, so the descriptive message is only
built in dev mode and production keeps throwing just the bare NG0500 code
without pulling validateMatchingNode's DOM-printing machinery into the
production bundle (verified via the bundling/hydration golden-symbols test,
which is unchanged).
(cherry picked from commit 4560f4fdcd)
Previously, the release script hardcoded the upstream repository URL as an unauthenticated HTTPS URL (https://github.com/angular/angular.git). Although the script verified that a GITHUB_TOKEN environment variable was present, it only used that token for REST API calls (such as creating the GitHub release) and did not provide it to Git commands. As a result, users who authenticate to GitHub via SSH (and do not have an HTTPS Git credential helper configured) were prompted interactively for GitHub login credentials when pushing the release tag.
This change dynamically resolves the upstream remote name from the user's configured remotes by checking for any remote pointing to angular/angular. When pushing over SSH, Git uses the user's existing SSH credentials. When pushing over HTTPS (or falling back), the script injects GITHUB_TOKEN into the push URL to prevent interactive authentication prompts.
(cherry picked from commit 6f848db435)
When resolving template symbols for SafePropertyRead in TCBs emitted with optional chaining (strictSafeNavigationTypes: true), SymbolBuilder falls back to finding a TS node matching the AST expression's nameSpan. It then traverses up through parent nodes to find the enclosing expression.
Previously, the traversal loop checked isAccessExpression(node.parent) without verifying whether node was the accessed member name or the expression receiver. When multiple optional navigation expressions are chained (e.g. route?.data?.['icon']), the parent of ((route)?.data) is an access expression where ((route)?.data) is the receiver. Because isAccessExpression was true, the loop continued ascending into the outer access expression, causing symbol resolution for data to erroneously return the symbol and TCB location of icon.
This commit refines the parent traversal condition so that it only climbs into a parent PropertyAccessExpression if node is the accessed name (node.parent.name === node), preventing escape into outer receiver expressions.
(cherry picked from commit e8aa222e7d)