是否可以在不彻底检查函数体中的每个参数的情况下缩小重载参数的类型? [英] Is it possible to narrow the types of overloaded parameters without exhaustively checking each parameter in the function body?

查看:86
本文介绍了是否可以在不彻底检查函数体中的每个参数的情况下缩小重载参数的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'd like to define a function which can accept parameters typed in one of two ways. For example:

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

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

Given this type signature, if abc is a number, then def is a string, and if abc is a string, then def is not defined. This is clear to humans, but is there any way for Typescript to recognize it? For example, the following implementation fails:

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 是不允许的.函数的调用者可以识别参数类型的分组,因此正如预期的那样,禁止以下内容:

because although the type of abc has been narrowed, TS doesn't understand that the type of def has been determined too, so def.includes is not permitted. The grouping of argument types is recognized for callers of the function, so the following is forbidden, as expected:

fn('abc', 'def');

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

But the overloaded type grouping seems to have no effect inside the function.

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

When there are only a couple of parameters, it's easy enough to explicitly (and redundantly) type-check each parameter, or use a type assertion for each once one has been checked, but that's still ugly. It gets much worse when there are more than a couple of parameters.

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

Another problematic redundancy is that each possible argument type needs to be listed not only in the type, but also in the function's parameter list. Eg (abc: number) and (abc: string) in the type definition also requires = (abc: number | string) in the parameter list.

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

Is there a better pattern available for function overloading without ditching it entirely? I know of at least two workarounds that don't involve overloading:

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

  • Pass an object of type { abc: number, def: string } | { abc: string } instead of multiple separate parameters, then pass the object go through a type-guard

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

Use two separate functions for the two different types of parameters

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

But I'd rather use overloading if there's a decent way to handle it.

推荐答案

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

All of your approaches can be reasonable: (1) separate function declarations (2) union of object parameters or (3) a function overload like in question.

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

I would prefer (1), If the caller already has enough information to decide which function has to be called, as this reduces the overall conditional complexity of fn body.

(2) 使用 区分联合类型更有意义,因此您不会对调用者进行过多的属性检查.实施例:

(2) makes more sense with a discriminated union type, so you don't loose excess property checks for the caller. 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 签名中列出两次类型.我在这里发现了这个老歌的原因:只有具有单个重载的函数才能应用上下文类型.

In addition, you don't have to list the types twice both in Fn and as part of fn signature. I found this oldie here as a reason: only functions with a single overload can apply a contextual type.

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

With (3) there is no clever way to handle variable function parameters inside the function. TS/JS don't support a function overload implementation like this:

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

因此,您将始终必须使用具有可选参数和/或联合类型的更宽的类型签名,并在函数体内缩小这些类型的范围.

So you will always have to use a wider type signature with optional parameters and/or union types and narrow these types down inside the function body.

这篇关于是否可以在不彻底检查函数体中的每个参数的情况下缩小重载参数的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆