silas / node-consul

Consul client
https://www.npmjs.com/package/consul
MIT License
559 stars 83 forks source link

Difficulties consuming the new types #162

Open brandonw opened 1 week ago

brandonw commented 1 week ago

I am having difficulties consuming the new types. One specific example is I save a Watch instance created via consul.watch(), but cannot properly specify the type in the class definition.

As someone who only has some Typescript experience, I think the problem may be that the new declaration files do not specify namespaces? This causes any top-down usage to be typed, but no types to be exported other than lib/index.d.ts's export of Consul.

So when typing things:

import Consul from 'consul';

class Foo {
  consul: Consul; // can type this because Consul is exported inline with the class itself
  watch?: Watch; // this fails to compile, because the Watch type is not exported.
}

// ...nside a Foo instance
// tsc knows that this.watch should be of type "Watch" since internally it has access to the type.
// However, there is no way to reference it in the above class definition.
this.watch = this.consul.watch({method, options});
UNIDY2002 commented 1 week ago

@brandonw You may try typing watch as InstanceType<typeof Consul.Watch>.

I overlooked the possibility of using shortcuts for these instance types. If needed, I’ll be happy to make further adjustments.

brandonw commented 1 week ago

@UNIDY2002 that works just as needed, thanks!

Another example that may need a different approach is referencing the type of the function args of an instance function consul.health.service().

This seems to almost be do-able via Parameters<InstanceType<typeof Consul.Health>['service']> with the exception that the function is overloaded:

  service(options: ServiceOptions): Promise<ServiceResult>;
  service(service: string): Promise<ServiceResult>;

I think if there was an additional general case override a la

  service(options: ServiceOptions): Promise<ServiceResult>;
  service(service: string): Promise<ServiceResult>;
  service(service: ServiceOptions | string): Promise<ServiceResult>;

Then it would be possible with a bit of effort?

Then again, there is probably some advanced type magic you can do in order to iterate over the overloads and then grab the first one explicitly, but that is beyond my current capabilities 😁

UNIDY2002 commented 1 week ago

@brandonw Thank you for your prompt feedback! I agree that referencing these types can be quite complex, creating obstacles for users. I’m exploring ways to implement shortcuts for these internal types to improve usability.

Additionally, since these type definitions are still in a relatively immature state, I welcome any further feedback regarding any inconvenient usage you may encounter!