New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(forms): adding typings to form components #32450
feat(forms): adding typings to form components #32450
Conversation
*/ | ||
public readonly value: any; | ||
public readonly value: T|null = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't undefined also a valid value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imho the value should match the type that was specified.
so, to reset it should use null and if the type is only composed of optional attributes you should pass an empty object.
or is there any special case where passing undefined might be important?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ultimately that doesn't matter, if you do not allow undefined here when it was allowed before, this is a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well if you don't specify a type T defaults to any which allows undefined which should pass, no?
so the current untyped code should not be affected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, true, any | null
will just collapse to any
anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value should really be DeepPartial<T>
. Even the docs say you only get the enabled controls of a FormGroup.
Because FormGroups can be nested, value can only contain a partial of each group.
I understand that emoji in commit messages are somewhat modern, but Angular doesn't use those and since the commit messages are compiled into the changelog I would consider removing it. |
Partially duplicate of #20040 |
the emoji comes from a small app that helps with commit messages (https://github.com/commitizen/cz-cli) but I can get rid of it |
I hadn't seen that one... |
- creating FormStatus enum (reusable) - adding typing to AbstractControl (value, status, valueChanges, statusChanges) - adding typing to FormControl (constructor, setValue, patchValue, reset) - adding typing to FormGroup (constructor, registerControl, addControl, removeControl, setControl, contains, setValue, patchValue, reset, getRawValue) - adding typing to FormArray (constructor, at, push, insert, setControl, setValue, patchValue, reset, getRawValue)
@@ -12,19 +12,26 @@ import {composeAsyncValidators, composeValidators} from './directives/shared'; | |||
import {AsyncValidatorFn, ValidationErrors, ValidatorFn} from './directives/validators'; | |||
import {toObservable} from './validators'; | |||
|
|||
export enum FormStatus { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export enum FormStatus { | |
export const enum FormStatus { |
@Waldbillig-Thomas can you try to get this PR green? |
i started to look into it, but I probably won't have time to continue until this weekend I say that there was an error when you created a formcontrol with null as value and no type, typescript infers that the type T is null and not any, which causes problems... and for the rest, I have to check what else is failing b.t.w. I am having some problems running scripts from the package.json locally. does anyone know where I can find some more information about this? resp. Is there an easier way to check out stuff locally? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As much as I want type checking for Angular forms but your pull request contains breaking changes.
When using typed forms Angular must support all valid use cases.
For some cases it's difficult to find the correct type. In my oppinion values should be of type DeepPartial<T>
. This is not correct for FormControls but is needed for FormControls or else not all operations are possible.
*/ | ||
public readonly value: any; | ||
public readonly value: T|null = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value should really be DeepPartial<T>
. Even the docs say you only get the enabled controls of a FormGroup.
Because FormGroups can be nested, value can only contain a partial of each group.
@@ -297,7 +303,7 @@ export abstract class AbstractControl { | |||
* the UI or programmatically. | |||
*/ | |||
// TODO(issue/24571): remove '!'. | |||
public readonly valueChanges !: Observable<any>; | |||
public readonly valueChanges !: Observable<T|null>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as with value
the type of valueChanges
should be a DeepPartial<T>
instead only type T
.
@@ -1014,7 +1020,7 @@ export class FormControl extends AbstractControl { | |||
* | |||
*/ | |||
constructor( | |||
formState: any = null, | |||
formState: T|null = null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FormControl also accepts an object with the type {value: T, disabled: boolean}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe extract to a type:
type FormState<T> = T | { value: T, disabled: boolean };
This can be used in more places as well
@@ -1072,13 +1078,13 @@ export class FormControl extends AbstractControl { | |||
* | |||
* @see `setValue` for options | |||
*/ | |||
patchValue(value: any, options: { | |||
patchValue(value: T extends Object ? Partial<T> : T, options: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can value be Partial<T>
? For FormControl setValue
and patchValue
are the same and should have the same type. Here we can remove required properties of T
. This should not be possible.
@@ -1424,7 +1430,7 @@ export class FormGroup extends AbstractControl { | |||
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity | |||
* updateValueAndValidity} method. | |||
*/ | |||
patchValue(value: {[key: string]: any}, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): | |||
patchValue(value: Partial<T>, options: {onlySelf?: boolean, emitEvent?: boolean} = {}): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
value
should be of type DeepPartial<T>
. With only Partial<T>
even the example for reactive forms in the official docs can not be typed correctly, because the method updateProfile()
uses a deep partial on a FormGroup (see docs).
@@ -1837,7 +1843,7 @@ export class FormArray extends AbstractControl { | |||
* The configuration options are passed to the {@link AbstractControl#updateValueAndValidity | |||
* updateValueAndValidity} method. | |||
*/ | |||
patchValue(value: any[], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void { | |||
patchValue(value: T[], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is tricky. If the FormArray contains only FormControls the type of T[]
is correct. But if the FormArray contains FormGroups the correct type is Partial<T>
, for nested FormGroups DeepPartial<T>
.
The type of T[]
is not backwards compatible.
@@ -1680,8 +1686,8 @@ export class FormArray extends AbstractControl { | |||
* | |||
*/ | |||
constructor( | |||
public controls: AbstractControl[], | |||
validatorOrOpts?: ValidatorFn|ValidatorFn[]|AbstractControlOptions|null, | |||
public controls: AbstractControl<T[]>[], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the type should be AbstractControl<T>[]
That is a very, very odd statement for a framework that chose TypeScript. A typed language never, ever attempts to support all legal untyped operations.
Between #16828/#20040 (2 years), #31963 (4 months), and #32450 (2 months), it'd be great to see a solution for TypeScript programming that deemed acceptable. |
@no0x9d didn't say that the Forms API need to support any shenanigan you can throw against it, but that it needs to support valid, existing use-cases, such as the different inputs these functions can take, dynamically adding and removing controls etc. And I definitely agree with that. Types are supposed to help us, not break APIs by not allowing us to pass valid input. |
And my point is that TypeScript "broke" JavaScript APIs, as do all types. [{name: 'one'}, {name: 'two'}].sort((a, b) => (b.name < a.name) - (a.name < b.name)); That is perfectly valid JavaScript. It is not valid TypeScript. If you want a type-checker to prove correct uses of types, it's not unreasonable to require different programming patterns than you might be without a type checker. Better types are of course better. But hopefully it will not take Angular another 2 years to arrive at those for something as exotic as forms. |
@Waldbillig-Thomas do you have plans on updating this PR any further? If not, I can branch off this and begin to try and address the issues above |
Currently I do not really have the time. You can close it for now and
if/when I have more time again I will create a new pull request
Thomas
…On Tue, 3 Mar 2020, 14:34 Sean Hale, ***@***.***> wrote:
@Waldbillig-Thomas <https://github.com/Waldbillig-Thomas> do you have
plans on updating this PR any further? If not, I can branch off this and
begin to try and address the issues above
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#32450?email_source=notifications&email_token=AAEK2UOE5ESQCOKCWWR7DSTRFUBPFA5CNFSM4ITAQIDKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENTP5TQ#issuecomment-593952462>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAEK2ULV35VNOV4QUN6VKVTRFUBPFANCNFSM4ITAQIDA>
.
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
currently there is no way to have typechecking for form elements
Issue Number: N/A
What is the new behavior?
PR Checklist
Does this PR introduce a breaking change?
Other information