【问题标题】:Can you overload a function based on a destructured parameter?你能重载一个基于解构参数的函数吗?
【发布时间】:2022-01-17 11:20:00
【问题描述】:

This example code 有点做作,但我正在尝试做类似的事情:

type ValueRequirement =
  | 'None'
  | 'Any String'
  | 'Any Number'
  | 'Another Widget\'s Value';

type Widget = {
  value: string
}

type WidgetValidationRule = {
  widget: Widget,
  valueRequirement: ValueRequirement,
}

function isANumber(value: string): boolean {
  throw new Error("Function not implemented."); // irrelevant implementation. Just trying to provide an example that can compile
}

function validate(widgetValidationRule: WidgetValidationRule): void {
  switch (widgetValidationRule.valueRequirement) {
    case 'None':
      return
    case 'Any Number':
      if (!isANumber(widgetValidationRule.widget.value)) {
        throw new Error('NaN');
      }
      return
    // ...
  }
}

我一直在慢慢地向ValueRequirement 添加更多值,我刚刚添加了一个新值'Another Widget\'s Value'。为此,我意识到我实际上无法检查这是否有效,除非 validate 传递了所有其他存在的 Widgets 的列表。

一方面,当且仅当ValueRequirement'Another Widget\'s Value' 时,我可以将allWidgets: Widget[] 添加为可选参数并在未定义的情况下抛出运行时异常。

我宁愿有一个编译时错误:如果ValueRequirement'Another Widget\'s Value',那么所有其他Widgets 的列表必须作为附加参数传入。否则传不进去。I imagine the solution would look something like this,但是我想不出办法让它编译:

type ValueRequirement =
  | 'None'
  | 'Any String'
  | 'Any Number'
  | 'Another Widget\'s Value';

type Widget = {
  value: string
}

type WidgetValidationRule = {
  widget: Widget,
  valueRequirement: ValueRequirement,
}

function isANumber(value: string): boolean {
  throw new Error("Function not implemented.");
}

function validate({widget, valueRequirement: 'Another Widget\'s Value'}: WidgetValidationRule, allWidgets: Widget[]): void;
function validate({widget, valueRequirement: 'None' | 'Any String' | 'Any Number'}: WidgetValidationRule): void;
function validate(widgetValidationRule: WidgetValidationRule, allWidgets?: Widget[]): void {
  //impl
}

一般来说有很多关于重载的类似问题,但在这种情况下,我必须解构一个类型并知道要重载的 ValueRequirement 值。

我一直在阅读有关如何使用 destructure objectsfunction overloading 的手册,但他们没有提到这种用例。我不确定这是否意味着不可能,我正在阅读文档的错误部分,或者这是一个很少有人问过的非常不寻常的问题(我在写这篇文章之前尝试了谷歌搜索)。

Destructuring with function overloading 的标题看起来很相似,但它是关于实现内部的解构。我说的是解构函数签名本身。

在 typescript 中,可以根据解构参数重载函数吗?


我想我要将ValueRequirementWidgetValidationRule 移动到validate 函数作为参数。那么这似乎很简单。无论如何,我很好奇这个问题的答案。

【问题讨论】:

  • this approach 是否满足您的需求?我不确定您是否真的关心重载或破坏,因为您的示例代码都没有显示。我试图给出一个“解构调用签名”,但没有实际的重载。或者你可以重载但你不需要解构。在这两种情况下,实现都无法理解ValueRequirement 属性与另一个函数参数的存在/不存在之间的相关性......
  • ...如果您真的需要在调用和实现方面都有效的东西,那么您需要像 this 这样的有区别的联合。如果你想让我写下这些作为答案,请告诉我。如果我遗漏了什么,请edit 提供一个真实的minimal reproducible example 来证明你的目标。
  • @jcalz 感谢您的帮助,但 jsejcksn 的答案正是我想要的。
  • 那么答案中的解构在哪里?我在这里错过了什么?
  • @jcalz 公平点。它不在那里。我认为解决我的问题需要解构。显然它没有。我在上面写了一些非编译代码来说明我认为我需要的解决方案类型。

标签: typescript types


【解决方案1】:

您可以通过使WidgetValidationRule 泛型来实现此目标(使用默认参数,以便您可以像现在一样继续使用它),然后根据泛型区分重载中的参数类型:

TS Playground

type ValueRequirement =
  | 'None'
  | 'Any String'
  | 'Any Number'
  | 'Another Widget\'s Value';

type Widget = {
  value: string
};

type WidgetValidationRule<T extends ValueRequirement = ValueRequirement> = {
  widget: Widget;
  valueRequirement: T;
};

function validate(widgetValidationRule: WidgetValidationRule<Exclude<ValueRequirement, 'Another Widget\'s Value'>>): void;
function validate(widgetValidationRule: WidgetValidationRule<'Another Widget\'s Value'>, otherWidgets: Widget[]): void;
function validate(widgetValidationRule: WidgetValidationRule, otherWidgets?: Widget[]): void {
  // implement
}

validate({
  widget: {value: 'one'},
  valueRequirement: 'None',
}); // ok

validate({
  widget: {value: 'one'},
  valueRequirement: `Another Widget's Value`,
});// Error (2322)

validate({
  widget: {value: 'one'},
  valueRequirement: `Another Widget's Value`,
}, [{value: 'hello'}, {value: 'world'}]); // ok

您也可以将联合类型拆分为逻辑组以使其更具可读性:

TS Playground

type IndependentValueRequirement =
  | 'None'
  | 'Any String'
  | 'Any Number';

type DependentValueRequirement = 'Another Widget\'s Value';

type ValueRequirement = IndependentValueRequirement | DependentValueRequirement;

type Widget = {
  value: string
};

type WidgetValidationRule<T extends ValueRequirement = ValueRequirement> = {
  widget: Widget;
  valueRequirement: T;
};

function validate(widgetValidationRule: WidgetValidationRule<IndependentValueRequirement>): void;
function validate(widgetValidationRule: WidgetValidationRule<DependentValueRequirement>, otherWidgets: Widget[]): void;
function validate(widgetValidationRule: WidgetValidationRule, otherWidgets?: Widget[]): void {
  // implement
}

【讨论】:

  • 有趣!这被认为是 hack 还是惯用语?
  • 惯用语。一点通用的抽象可以走很长的路。我会在一分钟内用一个清晰​​的重构来更新答案。
  • 谢谢!对于它的价值,我理解你的解决方案,我正计划按照你在第二个答案中建议的那样划分类型
猜你喜欢
  • 2016-06-04
  • 2014-01-19
  • 2021-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-25
  • 1970-01-01
相关资源
最近更新 更多