Typescript 函数 return 基于可选参数存在的类型而不使用函数重载

Typescript function return type based on optional parameter presence without using function overloads

Typescript playground

我的目标是根据可选 condition: "CONDITION" 参数的存在 return 不同的类型。我正试图在不诉诸重载的情况下实现这一目标。

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

没有超载的正确方法是什么?还是我把它复杂化了,在这种情况下,超载只是解决之道?

这里也有这个问题:

建议将接口用作 return 类型的映射,例如:

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;

但这也没有用。

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

function foo<T extends CONDITION | undefined>(condition?: T): T extends CONDITION ? TYPE_1 : TYPE_2 {
    if (condition) {
        //`as any` is intentional here: 
        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;