angular / angular/components

feat(dialog): Dialogs can define the type for their dialog data

Offen
#23,985 5 Kommentare 22 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
area: material/dialog feature needs: discussion P3
Vorherrschende Sprache
TypeScript
Sterne
25k
Forks
6.8k
Ø Merge
1 T. 8 Std.
Gemergte PRs (30 T.)
91

Beschreibung

### Feature Description

#4398 introduced a generic type argument for `MatDialogConfig`. This type argument is forwarded in `open`. However, the type is never tied in anyway to the actual component, making the type argument an essentially useless type assertion.

While due to the nature of Angular's dependency injection full type-safety cannot be achieved, it would be nice if at least the dialog component could declare the type it receives itself in such a way that a call to `open` will infer the correct type automatically. In order to achieve this, the dialog component needs to somehow expose this information. Here are a few possible solutions in no particular order, though others might exist:

#### Interface with type inference

We could define an interface and a helper type as follows:

```
interface MatDialogHasData {
data: T;
}

type ExtractDialogDataType = T extends MatDialogHasData ? D : any;
```

We can then adjust the type signature of `open` to infer the type accordingly:

```
open(
component: ComponentType,
data?: MatDialogConfig>
): MatDialogReg
```

Finally, a dialog component would be required to have a public field named `data` of the according type, which would typically be achieved through the injection itself:

```
constructor(@Inject(MAT_DIALOG_DATA) public data: MyDialogDataType) {}
```

*Note*: This change _is_ breaking because the generic signature of `open` changes and because dialog components could have an unrelated `data` field which would lead to an unintended type inference. Unfortunately we cannot prevent this here due to TypeScript's structural typing nature. In particular users would no longer be able to manually define the generic type argument. However see further below for a solution to this problem.

#### Interface with explicit method

Instead, we could also introduce an interface with an explicit method to be implemented:

```
interface MatDialogHasData {
getDialogData(): T
}
```

Otherwise the approach is largely similar. This change is only breaking in the generic type signature (I'm not sure whether you count this as a breaking change), however we could actually avoid that by changing our helper type to

```
type ExtractDialogDataType = T extends MatDialogHasData ? D : FALLBACK;
```

and then using the type argument as a fallback:

```
open(
component: ComponentType,
data?: MatDialogConfig>
): MatDialogReg
```

*Note*: The same could be done in the solution above. I believe we could make this a completely backwards-compatible change, excluding of course the exotic situation that a component happens to already have a `getDialogData` method for unrelated reasons. The only disadvantage here is forcing the component to provide a public method. In practice I don't consider this a concern as Angular already forces us to make many things public that shouldn't be public from a component API perspective.

---

On a very similar note, it would be nice to have the same for the inferred result type `R`, i.e. the component being able to define the type of data it can return, and have this information flow through the `open` method to the caller. The approaches mentioned above could, in theory, cover this as well.

### Use Case

Given some `MyDialogComponent` which expects dialog data of type `MyDialogData`, I would like

```
open(MyDialogComponent, {
data: {
// …
}
});
```

to type-check `data` against `MyDialogData` without having to manually specify `open`'s type arguments, but rather have this information flow from `MyDialogComponent`.

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Beginne damit, die in der Issue beschriebenen MatDialogConfig- und open-Typsignaturen sowie die vorgeschlagenen Ansätze mit MatDialogHasData und Ergebnistypen zu prüfen. Erledigt ist dies, wenn eine Dialogkomponente ihren Eingabedatentyp deklarieren kann, sodass open(MyDialogComponent, { data: ... }) eine Typprüfung durchläuft und ihn ohne manuelle Typargumente ableitet.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
angular, typescript
Bereich
frontend
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.