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 } }
(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!“
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) ❌ } }
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.
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)) { ... } } ... }
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.
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) ❌ } }
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
??
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 ✅ } }
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 ?.
Danke an Mateusz Burzyński (@AndaristRake) und Lenz Weber (@phry)
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 ✅ ... } }
connect("PROVIDER A", { ... }); connect("PROVIDER B", { ... }); ^ autocomplete works ✅
Die Sache ist also, dass wir ein Tupel (Array) mit genau den Typen zerstören, die wir wollen.
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 ✅ ... } }
connect("PROVIDER A", { ... }); connect("PROVIDER B", { ... }); ^ autocomplete works ✅
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 ✅ ... } }
connect("PROVIDER A", { ... }); connect("PROVIDER B", { ... }); ^ autocomplete works ✅
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!