简体   繁体   English

Function 返回类型取决于 function 参数属性

[英]Function return type that depends on function argument property

I'm creating a wrapper for an API in TypeScript.我正在为 TypeScript 中的 API 创建一个包装器。

One API call can return 2 types of responses: basic and extended, depending on the argument property.一次 API 调用可以返回两种类型的响应:基本响应和扩展响应,具体取决于参数属性。

If the argument property is true, the response will be extended.如果参数属性为真,响应将被扩展。 If the argument property is not provided or false, the response will be basic.如果未提供参数属性或为 false,则响应将是基本的。

This is the code example:这是代码示例:

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

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

However this code does not show the consumer of this method that an ExtendedResponse was returned, it shows the type as BasicResponse | ExtendedResponse但是,此代码并未向该方法的使用者显示返回了ExtendedResponse ,而是将类型显示为BasicResponse | ExtendedResponse BasicResponse | ExtendedResponse and does not show the extendedResponseProperty in intellisense. BasicResponse | ExtendedResponse并且不在智能感知中显示extendedResponseProperty

The closest I got to solving this was using this code:我最接近解决这个问题的方法是使用以下代码:

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

Link to playground: https://www.typescriptlang.org/play/index.html?ssl=32&ssc=86&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgIJQOYGdkG8CwAUMicmAPYDWEIAXMlmFKBgNxGnJQQbDl0MmLdsVJEAvkSKhIsRCgCiAD0ggAJhDXpsyCCppqc2nAVEk9qjWoBKELAAd+WCPSYBXCCIlTCM6PCRkACE4LGAEWwcnFFNOAEkAEXpGZhA2b0JpcH95ZGVLTUjHEGddfXUcELCIu2LS2NILA0La6IAFKHJ7aDAAT3pyACMAKwgEMBFJTMIYNxBxvhAuCDA3KBAi6IAKOEwsemMASnoq8M2SzyJZ+bBF5dX18+cdvfp85q0947zyqyfLmZzBb8e5rDatC4vbAHPbIAA+PwKn2w31ONSiF3hiI+-zwHFIwBgyChOFC2PUmiOeLMnFB6zwyES9AARDByENdsyADRlJH-DpdHr9PDiZDiEScKacfEkbgPJa4JnIVnswacqZTIgAGxWyywAEZkABeOngjHPXBkKg0FmhNQwbnLXj8FluLDMsWHLyEHVgPUAJmNpv+W0tFGoAmZdodPO4zsj0cdTQpNghzlcUA8ntYQA Link to playground: https://www.typescriptlang.org/play/index.html?ssl=32&ssc=86&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgIJQOYGdkG8CwAUMicmAPYDWEIAXMlmFKBgNxGnJQQbDl0MmLdsVJEAvkSKhIsRCgCiAD0ggAJhDXpsyCCppqc2nAVEk9qjWoBKELAAd+WCPSYBXCCIlTCM6PCRkACE4LGAEWwcnFFNOAEkAEXpGZhA2b0JpcH95ZGVLTUjHEGddfXUcELCIu2LS2NILA0La6IAFKHJ7aDAAT3pyACMAKwgEMBFJTMIYNxBxvhAuCDA3KBAi6IAKOEwsemMASnoq8M2SzyJZ+bBF5dX18+cdvfp85q0947zyqyfLmZzBb8e5rDatC4vbAHPbIAA+PwKn2w31ONSiF3hiI+-zwHFIwBgyChOFC2PUmiOeLMnFB6zwyES9AARDByENdsyADRlJH-DpdHr9PDiZDiEScKacfEkbgPJa4JnIVnswacqZTIgAGxWyywAEZkABeOngjHPXBkKg0FmhNQwbnLXj8FluLDMsWHLyEHVgPUAJmNpv+W0tFGoAmZdodPO4zsj0cdTQpNghzlcUA8ntYQA

But I don't really like this solution because the consumers of this method would need to deal with function overload and two types of input args.但我不太喜欢这种解决方案,因为这种方法的使用者需要处理 function 重载和两种类型的输入参数。

There is only one input Args, it just happens that it can have extendedResponse: boolean attribute. input Args只有一个,正好可以有extendedResponse: boolean属性。 Two input args make it not as intuitive.两个输入参数使其不那么直观。

Is there a TypeScript type-based approach that solves this?有没有基于 TypeScript 类型的方法可以解决这个问题?


UPDATE更新

A slightly better solution is to use a union type for the input 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 });

It's more readable for the consumer as it shows that adding callbackData: true will return a different result, but still not ideal because it is still two different argument types.它对消费者来说更具可读性,因为它表明添加callbackData: true将返回不同的结果,但仍然不理想,因为它仍然是两种不同的参数类型。

You could use a generic like this if you don't like the overloads:如果您不喜欢重载,可以使用这样的泛型:

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

But honestly, I like the code in your codesandbox.但老实说,我喜欢你的代码盒中的代码。 It describes what happens without additional magic and is simple to read.它描述了在没有额外魔法的情况下发生的事情,并且易于阅读。

Also, I'm not sure what you mean with "the consumer will have to deal with method overload" - the consumer gets what they are calling, not more or less.另外,我不确定“消费者将不得不处理方法重载”是什么意思——消费者得到他们正在调用的东西,而不是更多或更少。 You are describing a certain behaviour here, and no matter how you describe - different input arguments will lead to different output, and as soon as your runtime code does that, the consumer will have to deal with that anyways.您在这里描述了某种行为,无论您如何描述 - 不同的输入参数将导致不同的输出,一旦您的运行时代码这样做,消费者无论如何都必须处理它。

Interesting problem.有趣的问题。 I could only think of this solution.我只能想到这个解决方案。 I would use generics using only the extendedResponse field as type discriminator but it is more or less the same than previous answer:/我会使用 generics 仅使用 extendedResponse 字段作为类型鉴别器,但它或多或少与以前的答案相同:/

// We make the args generic of E 
interface Args<E extends true | false> {
    token: string;
    region: string;
    extendedResponse?: E;
}

// No changes here
interface BasicResponse { ID: string }
interface ExtendedResponse extends BasicResponse {
    extendedResponseProperty: Record<string, unknown>;
}

// This is not really required but I like to have explainatory intermediate types
type IReturnResponse<T extends boolean> = T extends true ? ExtendedResponse : T extends false ? BasicResponse : never;

/**
 * This function is now generic.
 * As a developer it might not be great but the implementation is not leaked to the user.
 */
function returnResponse<E extends boolean>(args: Args<E>): IReturnResponse<E> {
    if (args.extendedResponse === true) {
        return { ID: "foobar", extendedResponseProperty: {} } as IReturnResponse<E>;
    }
    return { ID: "foobar" } as IReturnResponse<E>;
}

// The library user will see this ⤵️


// Infered to Extended response
const res1 = returnResponse({
    token: "asdf",
    region: "us",
    extendedResponse: true,
});

// Infered to Basic response
const res2 = returnResponse({
    token: "asdf",
    region: "us",
    extendedResponse: false,
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Function 返回类型取决于可选参数 - Function return type depends on optional argument 依赖泛型参数作为 function 的值参数的类型 - Type that depends on generic argument as value argument to function 返回类型取决于泛型的 Typescript 函数 - Typescript function whose return type depends on a generic 创建 function 类型,其中返回类型取决于可选参数的存在 - Creating a function type where the return type depends on the existence of an optional parameter 期望模板化 function 参数中的嵌套属性取决于模板类型 - Expect a nested property in a templated function parameter that depends on the template type 如何表达依赖于其他属性值的函数类型 - How to express a function type that depends on the value of other property 根据作为参数传递的 function 的返回类型缩小 function 返回类型 - Narrow the function return type based on the return type of a function passed as an argument 打字稿:取决于字符串参数值的函数返回类型 - Typescript: Function return type that depends on value of string parameter 使用 TypeScript 使函数的返回类型取决于其参数? - Make return type of function depends on its arguments with TypeScript? 如何动态更改依赖于早期代码的函数返回类型? - How to dynamically change function's return type that depends on earlier code?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM