【问题标题】:Typescript function return type based on optional parameter presence without using function overloads基于可选参数存在而不使用函数重载的打字稿函数返回类型
【发布时间】:2021-01-23 08:14:13
【问题描述】:

Typescript playground

我的目标是根据可选的condition: "CONDITION" 参数的存在返回不同的类型。我正在尝试在不使用重载的情况下完成此任务。

type TYPE_1 = "TYPE_1"
type TYPE_2 = "TYPE_2"

type CONDITION = "CONDITION"

function foo(condition?: CONDITION): TYPE_1 | TYPE_2 {
    if (condition) {
        return "TYPE_1";
    }
    else {
        return "TYPE_2";
    }
}

const shouldBeType_1 = foo("CONDITION");    // ERROR: THIS IS BEING EVALUATED AS UNION TYPE: "TYPE_1" | "TYPE_2"
const shouldBeType_2 = foo();               // ERROR: THIS IS BEING EVALUATED AS UNION TYPE: "TYPE_1" | "TYPE_2"

这很容易通过重载来实现:

/* ########################################### */
/* #### THIS IS EASY TO DO WITH OVERLOADS #### */
/* ########################################### */

function foo_overloaded(): TYPE_2
function foo_overloaded(condition: "CONDITION"): TYPE_1
function foo_overloaded(condition?: "CONDITION"): TYPE_1 | TYPE_2 {
    if (condition) {
        return "TYPE_1";
    }
    else {
        return "TYPE_2";
    }
}

const overloaded_shouldBeType_1 = foo_overloaded("CONDITION");   // SUCCESS: THIS IS TYPE_1
const overloaded_shouldBeType_2 = foo_overloaded();              // SUCCESS: THIS IS TYPE_2

没有重载的正确方法是什么?还是我过于复杂了,而在这种情况下,重载只是一种方法?

这里也有这个问题:TypeScript: function return type based on argument, without overloading

它建议应该将接口用作返回类型的映射,例如:

interface Registry {
    A: number,
    B: string,
    C: boolean
}

function createType<K extends keyof Registry>(type: K, value: Registry[K]): Registry[K] {
    return value;
}

但我不能这样做,因为condition 要么是"CONDITION" | undefined。那么如何映射undefined 类型呢?我也尝试使用条件类型来做到这一点。比如:

type RETURN_TYPE<T extends undefined | "CONDITION"> = T extends "CONDITION" ? TYPE_1 : TYPE_2;

但这也没有用。

【问题讨论】:

    标签: typescript overloading typescript-typings optional-parameters


    【解决方案1】:

    我会说在这种情况下你会使用重载函数,你可以通过以下方式部分解决这个问题:

    function foo<T extends CONDITION | undefined>(condition?: T): T extends CONDITION ? TYPE_1 : TYPE_2 {
        if (condition) {
            //`as any` is intentional here: https://stackoverflow.com/questions/55641731/typescript-conditional-type-complains-type-not-assignable
            return "TYPE_1" as any;
        } else {
            return "TYPE_2" as any;
        }
    }
    

    有了这个,以下工作正常:

    const shouldBeType_1 = foo("CONDITION") // It is TYPE_1;
    

    但是当你不传递任何参数时它不会起作用:

    const shouldBeType_2 = foo(); // It is TYPE_1 | TYPE_2
    

    当然如果你直接传递undefined,它就可以正常工作:

    const shouldBeType_2 = foo(undefined); // It is TYPE_2;
    

    长话短说,现在,解决您的问题的最干净的方法是使用函数重载。


    编辑 正如已经指出的那样,如果我为泛型类型添加默认参数,这也适用于省略的参数。

    function foo<T extends CONDITION | undefined = undefined>(condition?: T): T extends CONDITION ? TYPE_1 : TYPE_2 {
        if (condition) {
            return "TYPE_1" as any;
        } else {
            return "TYPE_2" as any;
        }
    }
    
    // Both work:
    const shouldBeType_1 = foo("CONDITION") // It is TYPE_1;
    const shouldBeType_2 = foo(); // It is TYPE_2;
    
    

    【讨论】:

    • 谢谢,马丁。我总是在重载方面犹豫不决,因为我认为代码变得过于冗长,但我想这是一个很好的用途。不过,我会等几天看看是否有人提出了一些新想法。
    • 我明白你的意思,但在这种情况下,IMO 是最好的方法。
    • 您可以通过为T 设置默认值来修复undefined 的情况。即&lt;T extends CONDITION | undefined = undefined&gt;。然后返回类型都将是所需的。
    • 好点@ccarton,我已经更新了我的答案,包括你的修复。
    • 但是在那种情况下any 断言仍然是必要的?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-01
    • 2022-01-12
    • 1970-01-01
    • 2015-08-10
    • 2019-11-15
    相关资源
    最近更新 更多