Rumah  >  Artikel  >  hujung hadapan web  >  Ts Lanjutan: Parameter bergantung, kesatuan yang disimpulkan dan interaksi yang sihat di Twitter.

Ts Lanjutan: Parameter bergantung, kesatuan yang disimpulkan dan interaksi yang sihat di Twitter.

Barbara Streisand
Barbara Streisandasal
2024-10-02 22:30:03650semak imbas

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

Setiap kali saya menulis sebagai Foo dalam TypeScript, saya merasakan beratnya kekalahan.

Terdapat satu senario di mana perasaan ini sangat sengit: apabila fungsi mengambil parameter yang bergantung pada "mod" yang aktif.

lebih jelas dengan beberapa contoh kod:

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
  }
}

(Saya cuba menggunakan nama yang lebih realistik berbanding foo, goo, anjing dan kucing).

Jika anda telah meluangkan sedikit masa dengan TypeScript, anda mungkin mengesyaki kami pernah mengendalikan perkara ini sebagai ProviderAOpts, sebagai ProviderBOpts.


Tetapi ada masanya anda menghentak penumbuk anda di atas meja dan mendakwa: "Tiada lagi!"


1. Apa yang tidak berkesan

Perkara pertama yang selalu terlintas di fikiran saya dalam kes ini ialah menggunakan fungsi lebih muatan:

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) ❌
  }
}

Yang tidak berkesan. Tandatangan fungsi tidak disimpulkan dengan betul. Parameter pilihan sentiasa ProviderAOpts | ProviderBOpts. yang akan menyelesaikan kepada kesatuan bersama.

Ts tidak memautkan kedua-dua parameter dengan betul.


2. Perkara yang berfungsi tetapi tidak memautkan parameter

Alat seterusnya yang saya cuba ialah Jenis Predikat:

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)) {
        ...
      }
  }
  ...
}

Tetapi secara jujur, kami tidak menyelesaikan apa-apa. Kami baru sahaja memindahkan as di bawah permaidani ?. Memperkenalkan ifs tambahan dan, kami masih tidak memautkan parameter.


3. Apa yang tidak berkesan dan membuatkan saya menangis

Generik. Saya cuba menggunakan generik untuk memautkan parameter. Tidak berfungsi:

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) ❌
  }
}

Saya cuba bersungguh-sungguh dan berjaya setakat ini
Tapi akhirnya tak kisah pun
Saya terpaksa jatuh untuk kehilangan semuanya
Tapi akhirnya tak kisah pun
?


4. Apa yang berfungsi tetapi memaksa kita menukar tandatangan fungsi

Mengubah suai parameter opts menambahkan jenis pembekal melakukan silap mata:

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 ✅
  }
}

Ini adalah penyelesaian yang paling biasa, tetapi tidak selalu boleh menukar tandatangan fungsi. Atau mungkin anda tidak mahu. Soal prinsip ?.


Twitter untuk menyelamatkan

Terima kasih kepada Mateusz Burzyński (@AndaristRake) dan Lenz Weber (@phry)


kita boleh ke... ??

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 ✅
    ...
  }
}
5. Apa yang berfungsi: tupel yang dimusnahkan
connect("PROVIDER A", { ... });
connect("PROVIDER B", { ... });
                      ^ autocomplete works ✅

Jadi perkaranya ialah kami sedang memusnahkan tuple (array) dengan jenis yang tepat yang kami mahukan.


Satu-satunya kelemahan, jika kita memilih, menambah lebih banyak pasangan pada tupel... kita boleh mengekstrak jenis generik di sini:

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 ✅
    ...
  }
}
6. Apakah yang berfungsi: penyelesaian tuple umum
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 ✅
    ...
  }
}
7. TL;DR. COPY PASTE, TERIMA KASIH
connect("PROVIDER A", { ... });
connect("PROVIDER B", { ... });
                      ^ autocomplete works ✅

Terima kasih kepada Mateusz dan Lenz atas bantuan ?.

terima kasih kerana membaca ?. <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>

Atas ialah kandungan terperinci Ts Lanjutan: Parameter bergantung, kesatuan yang disimpulkan dan interaksi yang sihat di Twitter.. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn