(forms): Problems with typed forms and generics/inheritance.
See original GitHub issueWhich @angular/* package(s) are the source of the bug?
forms
Is this a regression?
No
Description
We have some complex forms throughout the apps where we use inheritance. We have created a custom typed forms at some point (quite long time ago) and now would like to switch to typed forms.
However, the types seem to be incorrectly defined as quite trivial code can’t be accepted by FormGroup
, for example. Here’s the sample code. Cannot create a stackblitz reproduction because for some reason I wasn’t able to find a typed FormGroup
and FormControl
even though the angular package versions in stackblitz were 14.1.1
.
export interface IAnimalBase {
isFish: FormControl<boolean>;
isNotFish: FormControl<boolean>;
}
export interface IAnimalForm<TAnimalBasics extends IAnimalBase = IAnimalBase> {
animalBasics: FormGroup<TAnimalBasics>;
}
export interface IDogForm extends IAnimalForm {
dogStuff: FormGroup<{ canBark: FormControl<boolean> }>;
}
class Test<TFG extends IAnimalForm> {
form: FormGroup<TFG>;
}
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run ng version
)
Angular CLI: 14.1.0
Node: 14.17.5
Package Manager: yarn 1.22.18
OS: win32 x64
Angular: 14.1.0
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, language-service, material, platform-browser
... platform-browser-dynamic, router
Package Version
------------------------------------------------------------
@angular-devkit/architect 0.1401.0
@angular-devkit/build-angular 14.1.0
@angular-devkit/build-ng-packagr 0.1002.0
@angular-devkit/core 14.1.0
@angular-devkit/schematics 14.1.0
@angular/flex-layout 14.0.0-beta.40
@schematics/angular 14.1.0
rxjs 7.5.6
typescript 4.6.3
Issue Analytics
- State:
- Created a year ago
- Comments:9 (3 by maintainers)
Top Results From Across the Web
Generic inheritance on Forms - MSDN - Microsoft
I have this situation: -Cne "Car" class. ... The result is an error on the Designer saying: "Cannot create an instance of BaseForm[T]...
Read more >c# - Visual Studio 2019 / Generic Forms + Inheritance Issues
1.) GenericArguments[0], 'Project.DBConnection.PurchaseOrder', on Project.Client.Forms.EntityPage'1[T]' violates the constraint of type ...
Read more >Issues with Generic Windows Forms inheritance ... - EMoreau
The .Net Framework itself has no issues at all with you inheriting a generic form because for the framework, a form is just...
Read more >Restrictions on Generics (The Java™ Tutorials > Learning the ...
Cannot Instantiate Generic Types with Primitive Types; Cannot Create Instances of Type ... For example, the following code causes a compile-time error:.
Read more >4 Problems of Angular 14 Typed Forms and How to Fix Them
You can't get FormGroup or FormArray from your models' types. But in real-world forms, bind with models. You should be able to construct...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Hi @liesahead,
The error TypeScript is reporting here is actually correct and valid - but it’s definitely not intuitive why this is the case.
To understand it, let’s first look at the constraint on
FormGroup
’s type:This says that the control group
TControl
must be an object, and for any property of that object (any key inkeyof TControl
), the value of that property must be anAbstractControl
instance.This makes sense -
TControl
is the control type for theFormGroup
, so its values should all beAbstractControl
s.We can see why this works:
We’re passing the interface
IAnimalBase
forTControl
, which has two keys (isFish
andisNotFish
), both of which have properAbstractControl
-compatible values. So this is accepted byFormGroup
’s generic constraint.Now, onto the example that doesn’t work:
This already causes an error -
FormGroup
is not happy with the type ofTAnimalBasic
. To understand whyFormGroup
is right not to accept this type, let’s look at an example of what could break if it did:What happened here:
BrokenAnimalControls
extendsIAnimalBase
and adds anisBroken
property alongsideisFish
andisNotFish
.isBroken
is NOT any kind of `AbstractControl - the forms system won’t know what to do with itIAnimalForm
didn’t mind us addingisBroken
- it only constrains us to extendIAnimalBase
, which we do.FormGroup
has no way of understanding or handling theisBroken
property, since it’s not a control.The problem is that even though the
FormGroup
constraint is valid forIAnimalBase
itself, that doesn’t guarantee it’ll be valid for any type derived fromIAnimalBase
. This is whyFormGroup
rejects such arbitrary derived types.To make this work, we need to enforce the same constraint as
FormGroup
aboutAbstractControl
values when declaring our derived type constraints:With that change,
TAnimalBasics
now provides the guarantee thatFormGroup
requires - all of its defined properties must haveAbstractControl
values, no matter what actual type is passed in to satisfy it.FormGroup
accepts it as a result.It might be easier to define a helper for this:
I hope this explanation makes sense - please let us know. There are definitely subtle things about the typed forms interfaces that are non-intuitive like this, and it’d be a good idea for us to have some documentation around the ones users like yourself end up encountering.
@alxhub , Maybe there’s a chance to somehow improve the generic forms implementation by using new typescript
satisfies
operator?