首页 > web前端 > js教程 > 高级 T:依赖参数、推断联合以及 Twitter 上的健康交互。

高级 T:依赖参数、推断联合以及 Twitter 上的健康交互。

Barbara Streisand
发布: 2024-10-02 22:30:03
原创
669 人浏览过

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

每次我用 TypeScript 写成 Foo 时,我都会感受到失败的沉重。

在一种情况下,这种感觉特别强烈:当函数采用的参数取决于哪个 "mode" 处于活动状态时。

通过一些示例代码更清晰:

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 来处理此问题。


但有时你会用拳头猛击桌子并宣称:“不再了!”


1. 什么不起作用

在这些情况下,我首先想到的总是使用函数重载

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) ❌
  }
}
登录后复制

这行不通。函数签名未正确推断。 options 参数始终为 ProviderAOpts |提供者B选择。这将解决共同联盟。

Ts 未正确链接两个参数。


2. 什么有效但没有链接参数

我尝试的下一个工具是类型谓词:

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)) {
        ...
      }
  }
  ...
}
登录后复制

但老实说,我们没有解决任何问题。我们只是把 as 移到了地毯下?引入了额外的 if,我们仍然没有链接参数。


3. 什么不起作用并且让我哭泣

泛型。我尝试使用泛型来链接参数。不起作用:

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) ❌
  }
}
登录后复制

我很努力,终于走到了这一步
但最终,这都不重要了
我必须跌倒才能失去一切
但最终,这都不重要了
?‍?


4. 什么有效但迫使我们更改函数签名

修改 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 ✅
    ...
  }
}
登录后复制
5. 什么有效:解构元组
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 ✅
    ...
  }
}
登录后复制
6. 什么有效:广义元组解决方案
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. 太长;博士。复制粘贴,谢谢
connect("PROVIDER A", { ... });
connect("PROVIDER B", { ... });
                      ^ autocomplete works ✅
登录后复制
登录后复制
登录后复制

感谢 Mateusz 和 Lenz 的帮助?.

感谢您的阅读?. <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>

以上是高级 T:依赖参数、推断联合以及 Twitter 上的健康交互。的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板