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

Barbara Streisand
Lepaskan: 2024-10-02 22:30:03
asal
655 orang telah melayarinya

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
  }
}
Salin selepas log masuk

(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) ❌
  }
}
Salin selepas log masuk

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)) {
        ...
      }
  }
  ...
}
Salin selepas log masuk

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) ❌
  }
}
Salin selepas log masuk

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 ✅
  }
}
Salin selepas log masuk

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

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 ✅
    ...
  }
}
Salin selepas log masuk
6. Apakah yang berfungsi: penyelesaian tuple umum
connect("PROVIDER A", { ... });
connect("PROVIDER B", { ... });
                      ^ autocomplete works ✅
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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 ✅
    ...
  }
}
Salin selepas log masuk
7. TL;DR. COPY PASTE, TERIMA KASIH
connect("PROVIDER A", { ... });
connect("PROVIDER B", { ... });
                      ^ autocomplete works ✅
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

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!

sumber:dev.to
Kenyataan Laman Web ini
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
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan