Heim > Web-Frontend > js-Tutorial > Fortgeschrittene Ts: Abhängige Parameter, abgeleitete Gewerkschaften und eine gesunde Interaktion auf Twitter.

Fortgeschrittene Ts: Abhängige Parameter, abgeleitete Gewerkschaften und eine gesunde Interaktion auf Twitter.

Barbara Streisand
Freigeben: 2024-10-02 22:30:03
Original
669 Leute haben es durchsucht

Advanced Ts: Dependent parameters, inferred unions and a healthy interaction on Twitter.

Jedes Mal, wenn ich als Foo in TypeScript schreibe, spüre ich die Last der Niederlage.

Es gibt ein Szenario, in dem dieses Gefühl besonders intensiv ist: Wenn eine Funktion einen Parameter annimmt, der davon abhängt, welcher "Modus" aktiv ist.

Klarer mit etwas Beispielcode:

type Provider = "PROVIDER A" | "PROVIDER B";
type ProviderAOpts = { ... };
type ProviderBOpts = { ... };

function connect(provider: Provider, options: ProviderAOpts | ProviderBOpts)  {
  switch (provider) {
    case "PROVIDER A":
      // options is ProviderAOpts
    case "PROVIDER B":
      // options is ProviderBOpts
  }
}
Nach dem Login kopieren

(Ich habe versucht, realistischere Namen anstelle von „Foo“, „Goo“, „Hund“ und „Katze“ zu verwenden).

Wenn Sie einige Zeit mit TypeScript verbracht haben, könnten Sie vermuten, dass wir dies früher mit ProviderAOpts und ProviderBOpts gehandhabt haben.


Aber manchmal schlägt man mit der Faust auf den Tisch und behauptet: „Nicht mehr!“


1. Was nicht funktioniert

Das erste, was mir in diesen Fällen immer in den Sinn kommt, ist die Verwendung von Funktionsüberladung:

function connect(provider: "PROVIDER A", options: ProviderAOpts): void;
function connect(provider: "PROVIDER B", options: ProviderBOpts): void;

function connect(provider: Provider, options: ProviderAOpts | ProviderBOpts) {
  switch (provider) {
    case "PROVIDER A":
    // (options as ProviderAOpts) ❌
    case "PROVIDER B":
    // (options as ProviderBOpts) ❌
  }
}
Nach dem Login kopieren

Was nicht funktioniert. Die Funktionssignatur wird nicht korrekt abgeleitet. Der Optionsparameter ist immer ProviderAOpts | ProviderBOpts. die sich zur gemeinsamen Union auflösen wird.

Ts verknüpft beide Parameter nicht korrekt.


2. Was funktioniert, aber nicht die Verknüpfung der Parameter ist

Das nächste Tool, das ich versuche, sind Typprädikate:

type ConnectOptions = ProviderAOpts | ProviderBOpts;

function isAOptions(options: ConnectOptions): options is ProviderAOpts {
  return (options as ProviderAOpts).$$$ !== undefined;
}

function isBOptions(options: ConnectOptions): options is ProviderBOpts {
  return (options as ProviderBOpts).$$$ !== undefined;
}

function connect(provider: Provider, options: ConnectOptions) {
  switch (provider) {
    case "PROVIDER A":
      if (isAOptions(options)) {
        ...
      }
    case "PROVIDER B":
      if (isBOptions(options)) {
        ...
      }
  }
  ...
}
Nach dem Login kopieren

Aber ehrlich gesagt haben wir nichts gelöst. Wir haben das einfach unter den Teppich geschoben? Zusätzliche ifs und eingeführt, wir verknüpfen die Parameter immer noch nicht.


3. Was nicht funktioniert und mich zum Weinen bringt

Generika. Ich habe versucht, Generika zu verwenden, um die Parameter zu verknüpfen. Funktioniert nicht:

function connect<T extends Provider>(
  provider: T,
  options: T extends "PROVIDER A" ? ProviderAOpts : ProviderBOpts
) {
  switch (provider) {
    case "PROVIDER A":
    // (options as ProviderAOpts) ❌
    case "PROVIDER B":
    // (options as ProviderBOpts) ❌
  }
}
Nach dem Login kopieren

Ich habe mir so viel Mühe gegeben und bin so weit gekommen
Aber am Ende spielt es keine Rolle
Ich musste fallen, um alles zu verlieren
Aber am Ende spielt es keine Rolle
?‍?


4. Was funktioniert, zwingt uns aber dazu, die Funktionssignatur zu ändern?

Das Ändern der opts-Parameter durch Hinzufügen des Anbietertyps reicht aus:

type Provider = "PROVIDER A" | "PROVIDER B";

type ProviderOptsBase = {
  provider: Provider;
}

type ProviderAOpts = ProviderOptsBase & {
  provider: "PROVIDER A";
  ...;
};

type ProviderBOpts = ProviderOptsBase & {
  provider: "PROVIDER B";
  ...;
};

function connect(options: ConnectOptions) {
  switch (options.provider) {
    case "PROVIDER A":
      // options is ProviderAOpts ✅
    case "PROVIDER B":
      // options is ProviderBOpts ✅
  }
}
Nach dem Login kopieren

Dies ist die häufigste Lösung, es ist jedoch nicht immer möglich, die Funktionssignatur zu ändern. Oder vielleicht möchten Sie es einfach nicht. Grundsatzfrage ?.


Twitter zur Rettung

Danke an Mateusz Burzyński (@AndaristRake) und Lenz Weber (@phry)


Wir können zu... ??

kommen

type Provider = "PROVIDER A" | "PROVIDER B";
type ProviderAOpts = { ... };
type ProviderBOpts = { ... };

function connect(
  ...[provider, options]:
    | ["PROVIDER A", ProviderAOpts]
    | ["PROVIDER B", ProviderBOpts]
) {
  switch (provider) {
    case "PROVIDER A":
      // options is ProviderAOpts ✅
    case "PROVIDER B":
      // options is ProviderBOpts ✅
    ...
  }
}
Nach dem Login kopieren
5. Was funktioniert: das destrukturierte Tupel
connect("PROVIDER A", { ... });
connect("PROVIDER B", { ... });
                      ^ autocomplete works ✅
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Die Sache ist also, dass wir ein Tupel (Array) mit genau den Typen zerstören, die wir wollen.


Der einzige Nachteil: Wenn wir wählerisch sind und dem Tupel weitere Paare hinzufügen, können wir hier einen generischen Typ extrahieren:

type Provider = "PROVIDER A" | "PROVIDER B";
type ProviderAOpts = { ... };
type ProviderBOpts = { ... };

type ProviderOpts = {
  "PROVIDER A": ProviderAOpts;
  "PROVIDER B": ProviderBOpts;
};

// solves to 
// ["PROVIDER A", ProviderAOpts] | ["PROVIDER B", ProviderBOpts]
type ConnectOptions = {
  [K in keyof ProviderOpts]: [K, ProviderOpts[K]];
}[keyof ProviderOpts]; 

function connect(...[provider, options]: ConnectOptions) {
  switch (provider) {
    case "PROVIDER A":
      // options is ProviderAOpts ✅
    case "PROVIDER B":
      // options is ProviderBOpts ✅
    ...
  }
}
Nach dem Login kopieren
6. Was funktioniert: verallgemeinerte Tupellösung
connect("PROVIDER A", { ... });
connect("PROVIDER B", { ... });
                      ^ autocomplete works ✅
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

type Provider = "PROVIDER A" | "PROVIDER B";
type ProviderAOpts = { ... };
type ProviderBOpts = { ... };

type ProviderOpts = {
  "PROVIDER A": ProviderAOpts;
  "PROVIDER B": ProviderBOpts;
};

// aux type to extract the key and the options from ProviderOpts
type KeyOpts<T> = {
  [K in keyof T]: [K, T[K]];
}[keyof T];


function connect(...[provider, options]: KeyOpts<ProviderOpts>) {
  switch (provider) {
    case "PROVIDER A":
      // options is ProviderAOpts ✅
    case "PROVIDER B":
      // options is ProviderBOpts ✅
    ...
  }
}
Nach dem Login kopieren
7. TL;DR. KOPIEREN EINFÜGEN, DANKE
connect("PROVIDER A", { ... });
connect("PROVIDER B", { ... });
                      ^ autocomplete works ✅
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Danke an Mateusz und Lenz für die Hilfe?.

Danke fürs Lesen?. <script> // Detect dark theme var iframe = document.getElementById('tweet-1840828253684056557-683'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1840828253684056557&theme=dark" } </script> <script> // Detect dark theme var iframe = document.getElementById('tweet-1840346445334864141-950'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1840346445334864141&theme=dark" } </script>

Das obige ist der detaillierte Inhalt vonFortgeschrittene Ts: Abhängige Parameter, abgeleitete Gewerkschaften und eine gesunde Interaktion auf Twitter.. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage