T Mitchell
T Mitchell

Reputation: 1017

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

Upvotes: 4

Views: 1583

Answers (1)

Nitzan Tomer
Nitzan Tomer

Reputation: 164129

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.

Upvotes: 6

Related Questions