2

Typescript supports function overloading, which is pretty cool, and it's possible to make a constant function overloaded like this:

interface FetchOverload {
  (action: string, method: 'post' | 'get'): object;
  (action: string): object
}

const fetch: FetchOverload = (action: string, method: 'get' | 'post' = 'get'): object { ... }

But let's suppose I want to inform the type of response expected, instead of any object. I can do this by simply replacing object with the desired type:

interface FetchOverload {
  (action: string, method: 'post' | 'get'): UserResponse;
  (action: string): UserResponse
}

const fetch: FetchOverload = (action: string, method: 'get' | 'post' = 'get'): UserResponse { ... }

If I want to go even further and instead of saying it can only return a response of type UserResponse, I could return a generic type:

interface FetchOverload<T> {
  (action: string, method: 'post' | 'get'): T;
  (action: string): T
}

const fetch: FetchOverload<T> = (action: string, method: 'get' | 'post' = 'get'): T { ... }

The problem is that for const there is no a generic type T. And in fact there isn't, I need to declare somewhere that T is a generic type, but I don't know where!

I tried everywhere and couldn't make it understand that there is a type T:

<T> const fetch: ...
const <T> fetch: ...
const fetch <T>: ...
const fetch: <T> FetchOverload<T> = ...
const fetch: FetchOverload<T> = <T> (action: ...)

The only solution I found was to transform the const function into native functions with overload:

function fetch<T>(action: string, method: 'post' | 'get'): T;
function fetch<T>(action: string): T;
function fetch<T>(action: string, method: 'post' | 'get' = 'get'): T { ... }

So what I would like to know is if in fact I need to use it this way, or if there is still some solution to keep using const.

David Rodrigues
  • 12,041
  • 16
  • 62
  • 90

1 Answers1

2

I "discovered" the answer as I ended up encountering a very similar problem in this question, this question and in this answer and this answer. Basically I would just do:

interface FetchOverload {
  <T> (action: string, method: 'post' | 'get'): T;
  <T> (action: string): T
}

export const fetch: FetchOverload = <T,> (action: string, method: 'get' | 'post' = 'get'): T { ... }

Actually, the only weird thing is that comma after the type, but it's mandatory, so I don't have much to do. But it's all settled!

David Rodrigues
  • 12,041
  • 16
  • 62
  • 90
  • 2
    For anyone wondering why it's mandatory, the comma's so that the parser doesn't confuse `` for a JSX element. – kelsny Sep 21 '22 at 04:46
  • 1
    AFAIK, the comma is mandatory in the TS playground because for some reason it treats it as JSX/TSX. It should not be needed if it's only set up to parse TS code. – VLAZ Sep 21 '22 at 04:57
  • you wouldn't happen to know what this type of interface declaration is called? Is there a special name, or is it just that typescript allows generic methods inside interface? – smac89 Sep 21 '22 at 05:11
  • 1
    @smac89 this is called [**Call Signatures**](https://www.typescriptlang.org/docs/handbook/2/functions.html#call-signatures). You can define it as a `type` or an `interface`. – David Rodrigues Sep 21 '22 at 16:18