angular/packages/forms/src
Dylan Hunn e441ff44b4 fix(forms): Prevent FormBuilder from distributing unions to control types. (#45942)
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
2022-05-10 12:36:37 -07:00
..
directives feat(forms): Implement strict types for the Angular Forms package. (#43834) 2022-04-12 17:37:04 +00:00
model docs(forms): Add documentation for typed forms. (#45841) 2022-05-02 15:03:40 -07:00
directives.ts perf(forms): make RadioControlRegistry class tree-shakable (#41126) 2021-03-16 09:35:14 -07:00
errors.ts refactor(forms): use shared RuntimeError class (#44398) 2022-01-06 23:43:19 +00:00
form_builder.ts fix(forms): Prevent FormBuilder from distributing unions to control types. (#45942) 2022-05-10 12:36:37 -07:00
form_providers.ts perf(forms): make RadioControlRegistry class tree-shakable (#41126) 2021-03-16 09:35:14 -07:00
forms.ts feat(forms): Add FormBuilder.nonNullable. (#45852) 2022-05-04 12:46:05 -07:00
util.ts refactor(forms): Split up model.ts. (#45217) 2022-03-01 19:49:31 +00:00
validators.ts docs(forms): remove the incorrect set value from previous commit (#45533) 2022-04-07 21:00:25 +00:00
version.ts docs: fix package name in version.ts files in different packages (#41208) 2021-05-10 10:26:34 -04:00