Skip to content
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

Support full structural subtyping/"duck typing" for typed forms #49374

Open
henry-alakazhang opened this issue Mar 9, 2023 · 2 comments
Open

Comments

@henry-alakazhang
Copy link

Which @angular/* package(s) are relevant/related to the feature request?

forms

Description

In TS you can take advantage of duck typing to assign objects that contain the full shape of another, for example:

value: { foo: string, bar: string };
subset: { foo: string };

subset = value;

This works through most generic types and classes as well (eg. a Promise<typeof value> can be used in place of a Promise<typeof subset>).

However, this doesn't work (most of the time) when assigning across FormGroups. This would be a useful feature for shareable/generic form components - they could take a FormGroup of a certain type, where some (or all) fields might be optional, but where the passing component can specify some of them.

Some examples are in this stackblitz: https://stackblitz.com/edit/angular-jgpu5s?file=src/main.ts

The error (specifically in the second example) suggests the type is forcing the assignability check in both directions regardless of which side is actually being assigned, while the two types are at least somewhat compatible (can be compared).

The only alternative at the moment is to use the least restrictive type for everything, which can add unneeded fields to the usages, and/or add unneeded and cumbersome optionality/nullability.

Wasn't sure whether to put this as a feature request or bug, since there might actually be a functional difference/design decision that I'm not understanding. Possibly (?) related to #47091 but that seemed a bit more specific and had some inheritence/extension complexity.

Proposed solution

Support this:

formGroup: FormGroup<{ foo: FormControl<string>; bar: FormControl<string> };
formGroupSubset: FormGroup<{ foo: FormControl<string> }>;

formGroupSubset = formGroup;

or at least this:

formGroup: FormGroup<{ foo: FormControl<string>; bar: FormControl<string> };
optionalFormGroup: FormGroup<{ foo?: FormControl<string>, bar?: FormControl<string> }>;

optionalFormGroup = formGroup;

Alternatives considered

N/A?

@ngbot ngbot bot added this to the needsTriage milestone Mar 9, 2023
@henry-alakazhang
Copy link
Author

I did a little investigating myself, and it looks like the cause is the IsAny helper used for distinguishing typed and untyped forms.

export type ɵIsAny<T, Y, N> = 0 extends(1&T) ? Y : N;

T being on the right hand of 0 extends(1&T) means all assignability checks have to go both ways. I can't reason about this well enough to tell if it's strange or not. lol.

I guess this means this is blocked on improved support TypeScript-side for any detection, or on removing IsAny? At a glance, it looks like it might be possible to keep the external types (mostly?) the same without conditional types, though I guess it might be difficult to replicate the extent of the any-ness in its current form.

@liesahead
Copy link

Would be nice if this gets some attention

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants