Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different return types based on optional function parameter - Typescript

I have a function that is a wrapper for axios.request.

I send a Message type along with config so that I can create a new Message with the response.data.

I have multiple types of Messages, so I am creating a generic TMessage type to handle this:

public request<TMessage extends BaseMessage<TMessage>>(
    config: Axios.AxiosXHRConfig<any>, 
    messageType: {new (message: string): TMessage}
): Promise<TMessage> {
    return new Promise((resolve, reject) => {
        axios.request(config).then((response: Axios.AxiosXHR<any>) => {
            try {
                let message = new messageType(response.data);
                resolve(message);
            } catch (err) {
                reject(err);
            }
        }, reject);
    });
}

I can now request with a message type and know what type of response I am getting:

RestClient.request(config, SomeMessage).then((response: SomeMessage) => {
  // response is of type SomeMessage, I have intellisense to help
  resolve(response);
});

I want to make this messageType optional, as some requests don't have a useful response and don't need to be instantiated into a new message.

Is there a way in TypeScript to do this? The following code uses Union Types and compiles, but it does not enforce the messageType to match the return type, making it a bit redundant.

I want a Promise<TMessage> return type if messageType is supplied. Otherwise I want a Promise<Axios.AxiosXHR<any>> return type

public request<TMessage extends BaseMessage<TMessage>>(
    config: Axios.AxiosXHRConfig<any>, 
    messageType?: {new (message: string): TMessage}
): Promise<TMessage | Axios.AxiosXHR<any>> {
  ...

-

RestClient.request(config, SomeMessage).then((response: OtherMessage) => {
  // this compiles, ideally it should complain about the mismatch of message types
  resolve(response);
});
like image 323
T Mitchell Avatar asked Sep 03 '25 15:09

T Mitchell


1 Answers

You can define different signatures for the method:

public request<TMessage extends BaseMessage<TMessage>>(
    config: Axios.AxiosXHRConfig<any>, 
    messageType: {new (message: string): TMessage}
): Promise<TMessage>;
public request(
    config: Axios.AxiosXHRConfig<any>
): Promise<Axios.AxiosXHR<any>>;
public request<TMessage extends BaseMessage<TMessage>>(
    config: Axios.AxiosXHRConfig<any>, 
    messageType?: {new (message: string): TMessage}
) {
    ...
}

Edit

This feature is described in the Overloads part of the docs:

JavaScript is inherently a very dynamic language. It’s not uncommon for a single JavaScript function to return different types of objects based on the shape of the arguments passed in

More info in the link.

like image 69
Nitzan Tomer Avatar answered Sep 05 '25 07:09

Nitzan Tomer