私たちは…に辿り着くことができます??
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 ✅
つまり、必要な正確な型でタプル (配列) を構造化しているということです。
唯一の欠点は、タプルにさらにペアを追加することにこだわる場合です...ここでジェネリック型を抽出できます:
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 ProviderOptstype KeyOpts= { [K in keyof T]: [K, T[K]];}[keyof T];function connect(...[provider, options]: KeyOpts ) { switch (provider) { case \\\"PROVIDER A\\\": // options is ProviderAOpts ✅ case \\\"PROVIDER B\\\": // options is ProviderBOpts ✅ ... }}
connect(\\\"PROVIDER A\\\", { ... });connect(\\\"PROVIDER B\\\", { ... }); ^ autocomplete works ✅
Mateusz と Lenz の協力に感謝します ?.
読んでいただきありがとうございます?.
","image":"http://www.luping.net/uploads/20241104/17307234156728be5752a02.jpg","datePublished":"2024-11-07T10:48:39+08:00","dateModified":"2024-11-07T10:48:39+08:00","author":{"@type":"Person","name":"luping.net","url":"https://www.luping.net/articlelist/0_1.html"}}この感情が特に強くなるシナリオが 1 つあります。それは、どの 「モード」 がアクティブであるかに応じて関数がパラメーターを受け取る場合です。
コード例を使用するとよりわかりやすくなります:
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 } }
(foo、goo、dog、cat ではなく、より現実的な名前を使用しようとしました)。
TypeScript を少し使ったことがある人なら、これを ProviderAOpts や ProviderBOpts として処理していたのではないかと疑うかもしれません。
しかし、テーブルに拳を叩きつけて主張するときがあります:「もうだめ!」
このような場合にいつも最初に思い浮かぶのは、関数のオーバーロード:
を使用することです。
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) ❌ } }
それはうまくいきません。関数のシグネチャが正しく推論されません。オプション パラメーターは常に ProviderAOpts | です。プロバイダーBOpts。それは共通の結合に解決されます。
Ts は両方のパラメータを正しくリンクしていません。
次に試すツールは Type Predicates:
です
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)) { ... } } ... }
しかし、正直に言うと、何も解決しませんでした。を敷物の下に移動しただけです?追加の if が導入されましたが、パラメータはまだリンクされていません。
ジェネリック。ジェネリックを使用してパラメータをリンクしようとしました。機能しません:
function connect( provider: T, options: T extends "PROVIDER A" ? ProviderAOpts : ProviderBOpts ) { switch (provider) { case "PROVIDER A": // (options as ProviderAOpts) ❌ case "PROVIDER B": // (options as ProviderBOpts) ❌ } }
頑張ってここまでたどり着きました
でも結局のところ、それは問題でもありません
すべてを失うには転ばなければならなかった
でも結局のところ、それは問題でもありません
??
opts パラメータを変更してプロバイダー タイプを追加するとうまくいきます:
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 ✅ } }
これは最も一般的な解決策ですが、関数のシグネチャを変更できるとは限りません。あるいは、単にそうしたくないかもしれません。原則の問題 ?.
Mateusz Burzyński (@AndaristRake) と 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 ✅
つまり、必要な正確な型でタプル (配列) を構造化しているということです。
唯一の欠点は、タプルにさらにペアを追加することにこだわる場合です...ここでジェネリック型を抽出できます:
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= { [K in keyof T]: [K, T[K]]; }[keyof T]; function connect(...[provider, options]: KeyOpts ) { switch (provider) { case "PROVIDER A": // options is ProviderAOpts ✅ case "PROVIDER B": // options is ProviderBOpts ✅ ... } }
connect("PROVIDER A", { ... }); connect("PROVIDER B", { ... }); ^ autocomplete works ✅
Mateusz と Lenz の協力に感謝します ?.
読んでいただきありがとうございます?.
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3