【问题标题】:Function return type that depends on function argument property取决于函数参数属性的函数返回类型
【发布时间】:2019-12-07 06:13:23
【问题描述】:

我正在为 TypeScript 中的 API 创建一个包装器。

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

如果参数属性为真,响应将被扩展。如果未提供参数属性或为 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

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

只有一个输入参数,它恰好可以有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 将返回不同的结果,但仍然不理想,因为它仍然是两种不同的参数类型。

【问题讨论】:

    标签: typescript


    【解决方案1】:

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

    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 });
    

    但老实说,我喜欢你的代码盒中的代码。它描述了在没有额外魔法的情况下会发生什么,并且易于阅读。

    另外,我不确定您所说的“消费者将不得不处理方法重载”是什么意思——消费者得到了他们正在调用的东西,而不是或多或少。您在这里描述了某种行为,无论您如何描述 - 不同的输入参数将导致不同的输出,并且一旦您的运行时代码这样做,消费者无论如何都必须处理它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-13
      • 1970-01-01
      • 2021-10-20
      • 2021-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多