Add typed key for `FormRecord`
- Dominant language
- TypeScript
- Stars
- 101k
- Forks
- 27.5k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 288
Description
### Which @angular/* package(s) are relevant/related to the feature request?
forms
### Description
Currently, the `FormRecord` only support typing the control, but not the key:
```ts
new FormRecord>({
key1: new FormControl(false, { nonNullable: true }),
key2: new FormControl(false, { nonNullable: true }),
key3: new FormControl(false, { nonNullable: true }),
});
```
Here the controls can only be `FormControl` but the keys of the controls can be anything (as long as it is a string).
But in some cases we want to key to be restricted to a specific set of strings (or Enum).
```ts
enum ControlKey {
Key1 = 'key1',
Key2 = 'key2',
Key3 = 'key3',
}
new FormRecord>({
key1: new FormControl(false, { nonNullable: true }),
key2: new FormControl(false, { nonNullable: true }),
key4: new FormControl(false, { nonNullable: true }), // I want the "key4" to throw a typing error here
});
```
This would be useful for known list of options that we can active/deactivate through a form.
The topic has already been discussed here:
- https://github.com/angular/angular/pull/45607#issuecomment-1098292360
- https://github.com/angular/angular/discussions/44513#discussioncomment-1991963
But it looks like it has never been considered as a new feature.
### Proposed solution
We could update the typing of `FormRecord` with something like this:
```ts
export class FormRecord<
TControl extends AbstractControl = AbstractControl,
TKey extends string = string
> extends FormGroup>> {}
export interface FormRecord {
registerControl(name: TKey, control: TControl): TControl;
addControl(name: TKey, control: TControl, options?: { emitEvent?: boolean }): void;
removeControl(name: TKey, options?: { emitEvent?: boolean }): void;
setControl(name: TKey, control: TControl, options?: { emitEvent?: boolean }): void;
contains(controlName: TKey): boolean;
setValue(value: Partial>>, options?: { onlySelf?: boolean, emitEvent?: boolean }): void;
patchValue(value: Partial>>, options?: { onlySelf?: boolean, emitEvent?: boolean }): void;
reset(value?: Partial>>, options?: { onlySelf?: boolean, emitEvent?: boolean }): void;
getRawValue(): Partial>>;
}
```
So that we can use it like this:
```ts
enum ControlKey {
Key1 = 'key1',
Key2 = 'key2',
Key3 = 'key3',
}
const form = new FormRecord, ControlKey>({
[ControlKey.Key1]: new FormControl(false, { nonNullable: true }),
[ControlKey.Key2]: new FormControl(false, { nonNullable: true }),
[ControlKey.Key3]: new FormControl(false, { nonNullable: true }),
});
```
### Alternatives considered
At the moment I'm using a `FormGroup` to match my need:
```ts
enum ControlKey {
Key1 = 'key1',
Key2 = 'key2',
Key3 = 'key3',
}
new FormGroup>>>({
[ControlKey.Key1]: new FormControl(false, { nonNullable: true }),
[ControlKey.Key2]: new FormControl(false, { nonNullable: true }),
[ControlKey.Key3]: new FormControl(false, { nonNullable: true }),
});
```
Contributor guide
Research direction
Start in the @angular/forms package by locating the FormRecord type and its control-management and value APIs. Review the linked pull request and discussion for prior constraints, then verify that typed keys are enforced across construction and named methods while existing string-key usage remains compatible.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- angular, typescript
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100