【问题标题】:Is it possible to narrow the types of overloaded parameters without exhaustively checking each parameter in the function body?是否可以在不详尽检查函数体中的每个参数的情况下缩小重载参数的类型?
【发布时间】:2020-05-01 20:56:36
【问题描述】:

我想定义一个函数,它可以接受以两种方式之一输入的参数。例如:

type Fn = {
    (abc: number, def: string): void,
    (abc: string): void,
};

给定这个类型签名,如果abc是一个数字,那么def是一个字符串,如果abc是一个字符串,那么def是没有定义的。这对人类来说是很清楚的,但是 Typescript 有没有办法识别它呢?例如,以下实现失败:

const fn: Fn = (abc: number | string, def?: string) => {
    if (typeof abc === 'string') console.log(abc.includes('substr'));
    else console.log(def.includes('substr'));
}

因为虽然abc的类型已经缩小,但是TS不理解def的类型也已经确定,所以def.includes是不允许的。函数的调用者可以识别参数类型的分组,因此正如预期的那样,以下是禁止的:

fn('abc', 'def');

但是重载的类型分组在函数内部似乎没有任何作用。

当只有几个参数时,很容易显式(且冗余地)对每个参数进行类型检查,或者在检查完一个参数后对每个参数使用类型断言,但这仍然很难看。当参数超过几个时,情况会变得更糟。

另一个有问题的冗余是每个可能的参数类型不仅需要在type 中列出,还需要在函数的参数列表中列出。例如,类型定义中的(abc: number)(abc: string)需要参数列表中的= (abc: number | string)

是否有更好的模式可用于函数重载而不完全放弃它?我知道至少有两种涉及重载的解决方法:

  • 传递{ abc: number, def: string } | { abc: string } 类型的对象而不是多个单独的参数,然后通过类型保护传递对象

  • 对两种不同类型的参数使用两个单独的函数

但我宁愿使用重载如果有一个不错的方法来处理它。

【问题讨论】:

    标签: typescript overloading


    【解决方案1】:

    您的所有方法都是合理的:(1) 单独的函数声明 (2) 对象参数的联合或 (3) 类似问题的函数重载。

    我更喜欢 (1),如果调用者已经有足够的信息来决定必须调用哪个函数,因为这会降低 fn 主体的整体条件复杂性。

    (2) 对discriminated union type 更有意义,因此您不会对调用者进行过多的属性检查。 Example:

    type OverloadParam =
        | { kind: "a", abc: number; def: string }
        | { kind: "b"; abc: string }
    
    type Fn = (arg: OverloadParam) => void
    
    const fn: Fn = (args) => {
        if (args.kind === "a") {
            args.def.includes('substr')
        } else {
            args.abc.includes('substr')
        }
    }
    

    此外,您不必在Fnfn 签名中列出两次类型。我在这里找到了 oldie 的原因:只有具有单个重载的函数才能应用上下文类型。

    使用 (3) 没有巧妙的方法来处理函数内部的可变函数参数。 TS/JS 不支持这样的函数重载实现:

    function fn(abc: number, def: string): void { }
    function fn(abc: string): void { } 
    // error (TS): Duplicate function implementation. JS would overwrite the first declaration
    

    因此,您将始终必须使用带有可选参数和/或联合类型的更广泛的类型签名,并将这些类型缩小到函数体内。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-11
      • 1970-01-01
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      • 2019-12-08
      • 2020-09-21
      • 1970-01-01
      相关资源
      最近更新 更多