取决于函数参数属性的函数返回类型

时间:2019-12-06 20:58:35

标签: typescript

我正在用TypeScript为API创建包装。

一个API调用可以返回两种类型的响应:基本响应和扩展响应,具体取决于参数属性。

如果arguments属性为true,则将扩展响应。如果不提供参数属性或为false,则响应将是基本的。

这是代码示例:

interface Args {
    token: string;
    region: string;
    extendedResponse?: boolean;
}

interface BasicResponse {
    ID: string;
}

interface ExtendedResponse extends BasicResponse {
    extendedResponseProperty: object;
}


function returnResponse(args: Args): BasicResponse | ExtendedResponse {
    if (args.extendedResponse) {
        return { ID: "foobar", extendedResponseProperty: {} };
    }
    return {ID: "foobar"}
}

let res1 = returnResponse({ token: "asdf", region: "us", extendedResponse: true });

游乐场:https://www.typescriptlang.org/play/index.html?ssl=24&ssc=84&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgIJQOYGdkG8CwAUMicmAPYDWEIAXMlmFKBgNxGnJQQbDl0MmLdsVIQAHpBAATCNIBKELAAd+WCAH56AI3LkANhDggRAXyJFQkWIhQAhOFmAJFKtSgKiSASQAi9RmYQNiJzQktwaHgkZABRSRpZBSVVEHVkCSlpHAcnFxT3PA4xBJk5V1T1AAUocmVoMABPenJtACsIBDAzC3DCGABXEC6+EC4IMAGoEAr3AAo4TCx6dGwASnpc51m0lAAfONKknfTPTmKSYBhkBaWAOkzE8oLdtaKvTnHJ6bxkP3oAEQwPTaRYAgA0GSOzzcuxqdQazTwpmQphEnDCnG43zGuH+yCBILBYTCREMYHGWAAjMgALxfKYzF7qOa4MhUGiAxzSGAQ8a8fiAgZYPmPMrJWHqehMAYoUxrES9XpAA

但是,此代码并未向该方法的使用者显示已返回ExtendedResponse,而是将其显示为BasicResponse | ExtendedResponse,并且在智能感知中未显示extendedResponseProperty

我最接近解决此问题的方法是使用以下代码:

interface Args {
    token: string;
    region: string;

}

interface ExtendedArgs extends Args {
    extendedResponse: true;

}

interface BasicResponse {
    ID: string;
}

interface ExtendedResponse extends BasicResponse {
    extendedResponseProperty: object;
}

function returnResponse(args: Args): BasicResponse;
function returnResponse(args: ExtendedArgs): ExtendedResponse;
function returnResponse(args: Args | ExtendedArgs): BasicResponse | ExtendedResponse {
    if (args as ExtendedArgs) {
        return { ID: "foobar", extendedResponseProperty: {} };
    }

    return {ID: "foobar"}
}

let res1 = returnResponse({ token: "asdf", region: "us" });

let res2 = returnResponse({ token: "asdf", region: "asdf", extendedResponse: true });

链接到游乐场:https://www.typescriptlang.org/play/index.html?ssl=32&ssc=86&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgIJQOYGdkG8CwAUMicmAPYDWEIAXMlmFKBgNxGnJQQbDl0MmLdsVJEAvkSKhIsRCgCiAD0ggAJhDXpsyCCppqc2nAVEk9qjWoBKELAAd+WCPSYBXCCIlTCM6PCRkACE4LGAEWwcnFFNOAEkAEXpGZhA2b0JpcH95ZGVLTUjHEGddfXUcELCIu2LS2NILA0La6IAFKHJ7aDAAT3pyACMAKwgEMBFJTMIYNxBxvhAuCDA3KBAi6IAKOEwsemMASnoq8M2SzyJZ+bBF5dX18+cdvfp85q0947zyqyfLmZzBb8e5rDatC4vbAHPbIAA+PwKn2w31ONSiF3hiI+-zwHFIwBgyChOFC2PUmiOeLMnFB6zwyES9AARDByENdsyADRlJH-DpdHr9PDiZDiEScKacfEkbgPJa4JnIVnswacqZTIgAGxWyywAEZkABeOngjHPXBkKg0FmhNQwbnLXj8FluLDMsWHLyEHVgPUAJmNpv+W0tFGoAmZdodPO4zsj0cdTQpNghzlcUA8ntYQA

但是我不太喜欢这种解决方案,因为此方法的使用者需要处理函数重载和两种类型的输入args。

只有一个输入Args,碰巧它可以具有extendedResponse: boolean属性。两个输入参数使它不那么直观。

是否存在可以解决此问题的基于TypeScript类型的方法?


更新

一个更好的解决方案是对输入的Args使用联合类型:

interface Args {
    token?: string;
    region?: string;
}


interface BasicResponse {
    ID: string;
}

interface ExtendedResponse extends BasicResponse {
    extendedResponseProperty: object;
}

function returnResponse(args: Args): BasicResponse;
function returnResponse(args: Args & {callbackData: true}): ExtendedResponse;
function returnResponse(args: Args | Args & {callbackData: true}): BasicResponse | ExtendedResponse {
    if (args as { callbackData: true }) {
        return { ID: "foobar", extendedResponseProperty: {}}
    }
    return {ID: "foobar"}
}

let res1 = returnResponse({ token: "asdf", region: "us" });

let res2 = returnResponse({ token: "foo", callbackData: true });

对于消费者而言,它更具可读性,因为它表明添加callbackData: true将返回不同的结果,但仍然不理想,因为它仍然是两种不同的参数类型。

1 个答案:

答案 0 :(得分:0)

如果您不喜欢重载,则可以使用这样的泛型:

interface Args {
    token: string;
    region: string;
    extendedResponse?: true;
}

interface BasicResponse {
    ID: string;
}

interface ExtendedResponse extends BasicResponse {
    extendedResponseProperty: object;
}

function returnResponse<A extends Args>(args: A): A extends { extendedResponse: true} ? ExtendedResponse : BasicResponse {
    if (args.extendedResponse === true) {
        return { ID: "foobar", extendedResponseProperty: {} } as any;
    }

    return {ID: "foobar"} as any
}

let res1 = returnResponse({ token: "asdf", region: "us" });

let res2 = returnResponse({ token: "asdf", region: "asdf", extendedResponse: true });

但是,老实说,我喜欢您codesandbox中的代码。它描述了在没有附加魔术的情况下会发生什么,并且易于阅读。

此外,我不确定您所说的“使用者必须处理方法重载”是什么意思-使用者获得了他们所调用的内容,或多或少。您在这里描述的是某种行为,无论您如何描述-不同的输入参数将导致不同的输出,并且只要您的运行时代码执行了此操作,使用者就必须处理该行为。