angular / angular/angular

Signal Forms [formField]: compiler only checks one direction, allowing unsound union FormValueControl bindings

Open
#70,093 5 comments 0 reactions 0 assignees View on GitHub
area: forms forms: signals
Dominant language
TypeScript
Stars
101k
Forks
27.5k
Avg merge
1d 19h
Merged PRs (30d)
288

Description

### Which @angular/* package(s) are the source of the bug?

compiler, forms

### Is this a regression?

No

### Description

A custom control implementing `FormValueControl` with a union value type silently
accepts a `[formField]` binding to a field whose type is only one member of that union.
The control can then write the other member into the field, changing the runtime shape
of the developer's model signal with no diagnostic at any point.
```ts
// App.ts
myModel = signal<{roles:string}>({roles:''})
myForm = form(this.myModel)
```
```html

```
```ts
// select.ts
export class MySelect
implements FormValueControl
{
readonly value = model();

```
> now i click a button changeToArray in the app-select component
```ts
//select.ts
changeToArray() {
this.value.set(['a', 'b']);
}
```


After the control calls `value.set(['a','b'])`, the source signal holds
`{ roles: ['a','b'] }` even though `roles` is declared `string`.

### So i can see runtime Type mainpulation happens

#### Questions:
so i did look at the code base abit and i found this
the customControlCreate where i thought it has no real inheritance of the `FormField` not generic where it can be passed

Image

> i opened the function it self, it has the parent as `FormField`
Image

honstly i did think abit with Gemini and reialized this might be not the fix
then gemini said, it is compiler issue and it did generate some suggestion fixes, Iam not an expert in this, so i made an artifact, I thought this **might help you guys And upload it**, so you can access it

**From here : => [angular-bug-Artifact](https://angular-bug.pplx.app/)**

Image

### Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/stackblitz-starters-nqxpezvp?file=package.json

### Please provide the exception or error you saw

```true
No, error were fired
```

### Please provide the environment you discovered this bug in (run `ng version`)

```true
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/

Angular CLI : 22.1.3
Angular : 22.1.0
Node.js : 22.22.3
Package Manager : npm 10.8.2
Operating System : linux x64

┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/aria │ 22.1.1 │ ^22.1.1 │
│ @angular/build │ 22.1.3 │ ^22.1.0 │
│ @angular/cli │ 22.1.3 │ ^22.1.0 │
│ @angular/common │ 22.1.0 │ ^22.1.0 │
│ @angular/compiler │ 22.1.0 │ ^22.1.0 │
│ @angular/compiler-cli │ 22.1.0 │ ^22.1.0 │
│ @angular/core │ 22.1.0 │ ^22.1.0 │
│ @angular/forms │ 22.1.0 │ ^22.1.0 │
│ @angular/platform-browser │ 22.1.0 │ ^22.1.0 │
│ @angular/router │ 22.1.0 │ ^22.1.0 │
│ rxjs │ 7.8.2 │ ^7.8.1 │
│ typescript │ 6.0.3 │ ~6.0.3 │
└───────────────────────────┴───────────────────┴───────────────────┘
```

### Anything else?

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with the StackBlitz reproduction and its package.json, then inspect the compiler and forms behavior around the [formField] binding shown in App.ts and select.ts. Reproduce the union binding and verify that the invalid one-member field receives a compiler diagnostic while valid bindings remain accepted.

Written by the indexing model from the issue text.

Assessment

Tech stack
angular, typescript
Domain
compilers, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.