nous pouvons arriver à... ??


5. Ce qui marche : le tuple déstructuré

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 ✅

Le fait est que nous déstructurons un tuple (tableau) avec les types exacts que nous voulons.

Le seul inconvénient, si nous sommes pointilleux, ajouter plus de paires au tuple... nous pouvons extraire un type générique ici :


6. Qu'est-ce qui fonctionne : solution de tuple généralisée

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 ✅

7. TL;DR. COPIER COLLER, MERCI

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 ✅

Merci à Mateusz et Lenz pour l'aide ?.

merci d'avoir lu ?.

","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"}}
"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Ts avancés : paramètres dépendants, unions déduites et interaction saine sur Twitter.

Ts avancés : paramètres dépendants, unions déduites et interaction saine sur Twitter.

Publié le 2024-11-07
Parcourir:512

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

Chaque fois que j'écris en tant que Foo en TypeScript, je ressens le poids de la défaite.

Il existe un scénario où cette sensation est particulièrement intense : lorsqu'une fonction prend un paramètre qui dépend du "mode" actif.

plus clair avec un exemple de code :

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

(J'ai essayé d'utiliser des noms plus réalistes plutôt que foo, goo, dog et cat).

Si vous avez passé du temps avec TypeScript, vous soupçonnez peut-être que nous gérions cela avec ProviderAOpts, comme ProviderBOpts.


Mais il y a un moment où vous frappez du poing sur la table et dites : "Pas plus !"


1. Ce qui ne fonctionne pas

La première chose qui me vient toujours à l'esprit dans ces cas est d'utiliser la surcharge de fonctions :

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

Ce qui ne marche pas. La signature de la fonction n'est pas déduite correctement. Le paramètre options est toujours ProviderAOpts | FournisseurBOpts. qui se résoudra à l’union commune.

Ts ne lie pas correctement les deux paramètres.


2. Qu'est-ce qui fonctionne mais ne relie pas les paramètres

Le prochain outil que j'essaie est Prédicats de type :

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

Mais honnêtement, nous n’avons rien résolu. On vient de déplacer le as sous le tapis ?. Introduction de ifs supplémentaires et nous ne lions toujours pas les paramètres.


3. Ce qui ne marche pas et qui me fait pleurer

Génériques. J'ai essayé d'utiliser des génériques pour lier les paramètres. Ne fonctionne pas :

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

J'ai essayé si fort et je suis arrivé jusqu'ici
Mais au final, ça n'a même pas d'importance
J'ai dû tomber pour tout perdre
Mais au final, ça n'a même pas d'importance
?‍?


4. Qu'est-ce qui fonctionne mais nous oblige à changer la signature de la fonction

Modifier les paramètres opts en ajoutant le type de fournisseur fait l'affaire :

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

C'est la solution la plus courante, mais il n'est pas toujours possible de modifier la signature de la fonction. Ou peut-être que vous ne veux le faire. Question de principes ?.


Twitter à la rescousse

Merci à Mateusz Burzyński (@AndaristRake) et Lenz Weber (@phry)

nous pouvons arriver à... ??


5. Ce qui marche : le tuple déstructuré

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 ✅

Le fait est que nous déstructurons un tuple (tableau) avec les types exacts que nous voulons.

Le seul inconvénient, si nous sommes pointilleux, ajouter plus de paires au tuple... nous pouvons extraire un type générique ici :


6. Qu'est-ce qui fonctionne : solution de tuple généralisée

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 ✅

7. TL;DR. COPIER COLLER, MERCI

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 ✅

Merci à Mateusz et Lenz pour l'aide ?.

merci d'avoir lu ?.

Déclaration de sortie Cet article est reproduit sur : https://dev.to/manuartero/advanced-ts-dependent-parameters-inferred-unions-and-a-healthy-interaction-on-twitter-13bc?1 En cas de violation, veuillez contacter study_golang@163 .comdelete
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3