0

I have the following code for making a request using the https module.

export const makeRequest = (requestOptions: RequestOptions, body?: string): Promise<string> =>
    new Promise((resolve, reject) => {
        const req = https.request(requestOptions, (res: IncomingMessage): void => {
            // TODO: This assumes the client wants a string--consider making generic
            res.setEncoding("utf8");
            let data = "";
            res.on("data", chunk => data += chunk);
            res.once("end", (): void => resolve(data));
        });
        req.once("error", error => reject(error));
        if (body) {
            req.write(body);
        }
        req.end();
    });

I'd like to make this generic in the type that's returned but to default to string, so I made changes and now have the following (which does not compile).

export interface OnDataAccumulator<T> {
    encoding: string;
    listener: (chunk: any) => void;
    result: () => T;
}

const makeOnDataStringAccumulator: () => OnDataAccumulator<string> = () => {
    let data = "";
    return {
        encoding: "utf8",
        listener: (chunk: any) => data += chunk,
        result: () => data
    };
};

export const makeRequest = <T = string>(requestOptions: RequestOptions,
                               accumulator: OnDataAccumulator<T> = makeOnDataStringAccumulator(),
                               body?: string): Promise<T> =>
    new Promise((resolve, reject) => {
        const req = https.request(requestOptions, (res: IncomingMessage): void => {
            res.setEncoding(accumulator.encoding);
            res.on("data", accumulator.listener);
            res.once("end", (): void => resolve(accumulator.result()));
        });
        req.once("error", error => reject(error));
        if (body) {
            req.write(body);
        }
        req.end();
    });

I assumed that the unlock for this was the type parameter <T = string> but instead I see the typescript error

Type 'OnDataAccumulator<string>' is not assignable to type 'OnDataAccumulator<T>'.
  Type 'string' is not assignable to type 'T'.
    'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
  • What's the correct approach for this?
  • From a consumer's perspective, would this be more complete for it to be generic in the body type?
  • Similarly, would it be more complete to provide an impl for the callbacks in each req.write and req.end?
  • Is "accumulator" the correct term for this object?
Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
geofflittle
  • 437
  • 1
  • 3
  • 14
  • `makeOnDataStringAccumulator` will only work when the type is instantiated with `string`. instead of a default type parameter I think you want to use an overloaded function signature. – Aluan Haddad Dec 14 '19 at 23:44
  • I think default parameters are generally recommended over method overloading in Typescript https://stackoverflow.com/a/12688457. I know that generic parameter defaults are possible in TS so I think what I have is close but is missing some small detail. – geofflittle Dec 16 '19 at 12:43
  • Yes, they are recommended but they don't cover all scenarios such as this. Yuu can't use them here because the default value of the second argument is only compatible when the type parameter is instantiated with its default. You can see this clearly if you write the single signature in a declaration only context. Does that make sense? – Aluan Haddad Dec 16 '19 at 14:42

0 Answers0