Alexander Zeitler
Alexander Zeitler

Reputation: 13109

Implement class method based on interface

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?

Upvotes: 1

Views: 120

Answers (2)

GregL
GregL

Reputation: 38151

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.

Upvotes: 1

jcalz
jcalz

Reputation: 330216

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

Upvotes: 2

Related Questions