【问题标题】:Overloaded return type not properly working重载的返回类型不能正常工作
【发布时间】:2021-06-16 18:05:37
【问题描述】:

my question 之后,我现在面临返回类型重载的问题。

interface A {
  a: number,
  b: string
}

function myFunc(...sIs: Array<number>): Map<number, A[]>;
function myFunc(sIs: Array<number>): Map<number, A[]>;
function myFunc(s: Array<number> | number, ...sIs: Array<number>): A | Map<number, A[]> {

}

我正在重载函数,所以当只有一个参数(即myFunc(1))时,返回值是单个数据¿或未定义?,而如果输入是一个数组,它会返回一个基于函数参数的映射。

我面临的问题是Conversion of type 'Map&lt;number, A[]&gt;' to type 'A' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

如果我不包含 undefined 作为潜在的返回值,我已经设法通过以下方式解决问题:

const info: any = await myFunc(5);
const snfo: A = info as A;

但是如果我包含像A | Map&lt;number, A[]&gt; | undefined 这样的返回值,就没有办法做到这一点。我知道简单的方法是始终返回 Map,但我想学习如何正确地做到这一点。

【问题讨论】:

  • &lt;Map&lt;number, A[]&gt;(第一个签名上的返回类型注释)是什么意思?此外,您的实现签名将 A 作为其可能的返回类型之一,但您的重载都没有...?
  • 重载的返回类型与实现的返回类型不匹配。其中之一必须返回 A 或者您的实现必须只返回地图类型。
  • @T.J.Crowder 我修正了它......这是一个错字
  • @Silvermind 这就是重点。如果输入参数只是一个单数,我想返回 A ,但如果输入参数是数组或数字序列,我想返回 Map 。这可能吗?
  • b的值从何而来?

标签: typescript return overloading


【解决方案1】:

在你说过的评论中:

如果输入参数只是一个单数,我想返回A,但如果输入参数是数组或数字序列,我想返回Map

所以让我们分别写出每一个。接受一个数字并返回一个A

// First signature
function myFunc(x: number): A;

接受一个数字数组并返回一个Map&lt;number, A&gt;(我假设您的意思是Map&lt;number, A&gt;,而不是Map&lt;number, A[]&gt;,这将是将数字映射到A数组):

// Second signature
function myFunc(x: number[]): Map<number, A>;

最后,接受任意数量的离散number 参数并返回Map&lt;number, A&gt;。通常你会这样写:

// (Probably won't use this one, keep reading)
function myFunc(...x: number[]): Map<number, A>;

...但是因为我们已经有了xnumbernumber[] 的签名,拥有该签名可能会使实现变得复杂,所以让我们改用它:

// Third signature
function myFunc(x: number, ...y: number[]): Map<number, A>;

现在我们要写实现签名,就是上面三个结合起来:

// Implementation signature
function myFunc(x: number | number[], ...y: number[]): A | Map<number, A>

全部一起,有一个示例实现:

interface A {
    a: number,
    b: string
}

function myFunc(x: number): A;
function myFunc(x: number[]): Map<number, A>;
function myFunc(...x: number[]): Map<number, A>;
function myFunc(x: number | number[], ...y: number[]): A | Map<number, A> {
    if (typeof x === "number") {
        if (y.length === 0) {
            // Just a single number was provided
            return makeA(x);
        }
        // Multiple discrete numbers
        x = [x, ...y];
    }
    return new Map(x.map(a => [a, makeA(a)]));
}

支持以下所有内容:

myFunc(42);
myFunc([1, 2]);
myFunc(3, 4);

Playground link

【讨论】:

    【解决方案2】:

    您没有正确使用函数重载。

    简而言之,对于使用该函数的每种不同方式,函数都应该有一个重载签名。然后实现必须实现所有重载的所有可能性的超集。

    重要的是实现是不可调用的。只有重载签名可以直接调用。

    一个简单的例子:

    function foo(val: number[]): number[]
    function foo(val: number): number
    function foo(val: number | number[]): number | number[] {
       return val
    }
    
    foo([1,2,3]) // [1,2,3], type is number[]
    foo(1) // 1, type is number
    

    这意味着您没有接受number 作为参数并返回A 的函数签名。

    你想要更接近:

    function myFunc(sIs: number[]): Map<number, A[]>;
    function myFunc(sIs: number): A;
    
    function myFunc(s: number[] | number): A | Map<number, A[]> {
      if (Array.isArray(s)) {
        // process array, return Map<number, A>
      } else {
        // process single value, return A
      }
    }
    
    const a = myFunc(1) // A
    const b = myFunc([1,2,3]) // Map<number, A>
    

    第一个重载处理数组,第二个处理单个值,实现处理(和返回)两者。

    Playground


    支持多个参数foo(1,2,3) 作为一个数组确实变得更加棘手。但我建议不要这样做。在我看来,它是一个相当笨重的 API。

    【讨论】:

    • 您在解释中包含的代码与我的代码相似。问题是当我打电话给const a = myFunc(1) 时,我得到了我显示的错误。我认为最明智的解决方案是删除将数组作为多个参数的选项。我也会简化代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-21
    • 2018-06-29
    • 2012-07-12
    相关资源
    最近更新 更多