1

Given this interface from 3rd party library type definitions:

interface IHttpResponse<T> {
  data: T;
  status: number;
  headers: IHttpHeadersGetter;
  config: IRequestConfig;
  statusText: string;
  xhrStatus: 'complete' | 'error' | 'timeout' | 'abort';
}

with IHttpHeadersGetter being this:

interface IHttpHeadersGetter {
  (): { [name: string]: string; };
  (headerName: string): string;
}

how can I implement headers in a class that implements IHttpResponse<T>?

This is what my class implementation looks like but as one can see, not all members from IHttpHeadersGetter are implemented:

class MockResponse<T> implements IHttpResponse<T> {
  data: T;  
  status: number;
  headers (headerName: string): string {
    // return some header value
  };
  config: IRequestConfig;
  statusText: string;
  xhrStatus: "complete" | "error" | "timeout" | "abort";
}

Thus, tsc complains about this:

error TS2345: Argument of type 'MockResponse<any>' is not assignable to parameter of type 'IHttpResponse<any>'.
  Types of property 'headers' are incompatible.
    Type '(headerName: string) => string' is not assignable to type 'IHttpHeadersGetter'.

How do I have to implement headers correctly?

2 Answers 2

2

The type of IHttpHeadersGetter has two call signatures. That means it's an overloaded function, and any implementation of it needs to be compatible. The easiest way to do this is to make the implementation itself an overloaded function with the same call signatures as that of IHttpHeadersGetter.

For example (excluding other properties):

class MockResponse<T> implements IHttpResponse<T> {

    // CALL SIGNATURES
    headers(): { [name: string]: string }; // call signature 1
    headers(headerName: string): string; // call signature 2

    // IMPLEMENTATION
    headers(headerName?: string): string | { [name: string]: string } {
        const theHeaders: { [name: string]: string } = {
            someHeader: "hello",
            otherHeader: "you"
        }
        return (typeof headerName !== "string") ? theHeaders :
            (headerName in theHeaders) ? theHeaders[headerName] : "";
    };
}

This should compile for you and work as desired. Hope that helps; good luck!

Link to code

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, this works now. I tried exactly that // IMPLEMENTATION part signature already but I didn't know about the // CALL SIGNATURES part being required. Are there some official docs about this?
The handbook section I linked above is probably the closest thing to "official" docs about it, and it kind of mentions this in there, but it's not very explicit about the implementation signature being different from the call signatures.
1

The key part is to define both overloads of the headers function on the class, and then implement the actual method using a type signature that is the two overloads combined.

The final class should be:

class MockResponse<T> implements IHttpResponse<T> {
  data: T;  
  status: number;
  // specify the two overloads first
  headers(): { [name: string]: string; };
  headers(headerName: string): string;
  // then implement the method combining both signatures
  headers (headerName?: string): string | { [name: string]: string; } {
    // return some header value
    if (typeof headerName === 'string') {
      return headerName;
    }
    return { some: 'object' };
  };
  config: IRequestConfig;
  statusText: string;
  xhrStatus: "complete" | "error" | "timeout" | "abort";
}

See it on typescriptlang.org playground.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.