2

I'm trying to work out how to do what I think would be called a 'nested type assertion'.

Typescript will allow you to assert an empty object through an interface to pose as the type of the interface. For example:

const fakeRequest = <ExpressCore.Request>{ protocol: 'test-protocol' };

Let's me specify an object with the protocol field set that acts as an ExpressCore.Request type (which has many properties- https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express-serve-static-core/index.d.ts#L177). I can use this conveniently to take a Request object and make an assertion about the protocol.

When I try to do this with a more complex interface, for example, the one provided for Request - https://github.com/request/request - the compiler tells me I have to provide three interior types:

Generictype 'RequestAPI<TRequest, TOptions, TUriUrl> requires 3 type argument(s).

I'm doing this using the @types/request typings available via DefinitelyTyped.

In the declaration file, RequestAPI is defined by:

export interface RequestAPI<TRequest extends Request, TOptions extends CoreOptions, TUriUrlOptions> {

Having worked on this, I've found examples of the syntax required to assert an object through an interface requiring one interior interface:

const pos = <GeoJSON.Feature<GeoJSON.GeometryObject>>{
  "type": "Feature",
  "properties":{},
  "geometry": {
    "type": "Point",
    "coordinates": [0, 1]
  }
};

See What means "Generic type 'Feature<T>' requires 1 type argument(s)" in Typescript?.

I cannot, however, work out how to do this with three interior interfaces. The below attempts have failed:

const foo = { {} as request.Request, {} as request.CoreOptions, {} as request.RequiredUriUrl } as request.RequestAPI;

const fakeRequester = <request.RequestAPI<request.Request><request.CoreOptions><request.RequiredUriUrl>> {{}, {}, {}};

etc..

If anyone knows how to handle a nested, multiple assertion or could point out what I'm doing wrong, it'd be appreciated.

nicholasf
  • 281
  • 2
  • 6

1 Answers1

2

I had to do some reading about generic type parameters but, eventually, it was doable.

To work out what was going on I used ts-node and built small interfaces that extended each other and others that used them as generic parameters.

Eventually I was able to mock the Request object in my test; for example:

    const fn: (options: (request.CoreOptions & request.UriOptions), callback?: request.RequestCallback) => any =
        function(options: (request.CoreOptions & request.UriOptions), cb?: request.RequestCallback) {
            expect(options.uri).to.equal('http://foo/id-xyz');
            done();
            return <request.Request>{};
        };

    const fakeRequester = <request.RequestAPI<request.Request, request.CoreOptions, {}>>fn;

    const agent = _agent(fakeRequester);

    agent('http', '/foo/id-xyz', { 'Accept-Encoding': 'gzip' }, fakeWritable(), (err, result) => {});
});
nicholasf
  • 281
  • 2
  • 6