Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(forms): Prevent FormBuilder from distributing unions to control t…
…ypes. 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.
- Loading branch information