mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
Previously, using `FormBuilder` with a union type would produce unions of *controls*:
```
// `foo` has type `FormControl<string>|FormControl<number>`.
const c = fb.nonNullable.group({foo: 'bar' as string | number});
```
This actually works in many cases, due to how extraordinarily powerful Typescript's distributive types are (e.g. `value` still has type `string|number`), but it is subtly incorrect. Here is a code example that exposes the reason the inference is incorrect. It exploits the fact that Typescript will not "un-distribute" a type, producing an obviously spurious error:
```
// fc gets an inferred distributive union type `FormControl<string> | FormControl<number>`
let fc = c.controls.foo;
// Error: Type 'FormControl<string | number>' is not assignable to type 'FormControl<string> | FormControl<number>'.
fc = new FormControl<string|number>('', {initialValueIsDefault: true});
```
Instead, we want the union to apply to the *values*:
```
// `foo` should have type `FormControl<string|number>`.
const c = fb.nonNullable.group({foo: 'bar' as string | number});
```
Essentially, we want to prevent Typescript from distributing the type. [As specified in the handbook](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types):
> Typically, distributivity is the desired behavior. To avoid that behavior, you can surround each side of the extends keyword with square brackets.
This PR applies this suggestion to `FormBuilder`'s type inference.
Fixes #45912.
PR Close #45942
|
||
|---|---|---|
| .. | ||
| src | ||
| test | ||
| BUILD.bazel | ||
| index.ts | ||
| package.json | ||
| PACKAGE.md | ||
| public_api.ts | ||