Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Proposition] Overloading interfaces with varying constraints #7083

Closed
niieani opened this issue Feb 15, 2016 · 2 comments
Closed

[Proposition] Overloading interfaces with varying constraints #7083

niieani opened this issue Feb 15, 2016 · 2 comments

Comments

@niieani
Copy link

niieani commented Feb 15, 2016

It's currently possible to do this:

  method<A extends Array<number>>(one):number;
  method<A extends Array<string>>(two):string;

But not possible to do this:

interface Example<T extends Array<number>> {
  method(one):number;
}
interface Example<T extends Array<string>> {
  method(two):string;
}

This means we cannot have different typings depending on the generic type constraint of the interface.
I've even tried a hack like this:

interface Example<T extends Array<any>> {
  method<A extends T & Array<number>>(one):number;
  method<A extends T & Array<string>>(two):string;
}

I was hoping that TS will infer the type of the T array from the intersect, unfortunately that does not happen (it offers both methods, regardless of whether Example is an instance of Example<Array<number>> or of Example<Array<string>>).

Among other uses, this feature would be useful for a variety of database libraries that return internal types that can have their own operations done on them. For example, when I map an DbArray<DbNumber>, I should get a different set of methods than when operating on DbArray<DbString>.

The current workaround is to ask the user to explicitly declare both - the outer and the inner type, e.g.:

interface Example<TOuter, TInner> {
  map<TOut>((item: TInner) => TOut): TOut;
  // ...TOuter used in another method
}

However, this TInner is redundant (it's already inside of TOuter) and sometimes not relevant at all.
With more complex types this can cause the introduction of even more generic types, that grow like cancer in the declarations (interface Example<A, B, C, D, ...>) - the more possibilities there are, the more explicit declarations need to be done at the interface level. This has been an obstacle to creating proper type definitions for RethinkDB DefinitelyTyped/DefinitelyTyped#4551.

Last example: an interface DbValue can be a generic of any primitive type in the database: an array, a string or a number.
For an instance of an array, you'd need to pass in DbValue<Array<number>, number>, yet for an instance of string, the second parameter is irrelevant: DbValue<string, void>.

What we'd need is to be able to constrain not only by method's own generics, but by the containing interfaces generics, for method signatures themselves, something like:

interface Example<T> {
  // here the constraint is not of the method, but of the type
  method(one):number where T extends Array<number>;
  method(two):string where T extends Array<string>;
}
@DanielRosenwasser
Copy link
Member

Is this a duplicate of #1290?

@niieani
Copy link
Author

niieani commented Feb 17, 2016

@DanielRosenwasser Yes! I did a search before posting but nothing came up. Thanks. Brilliant that there's already an issue for that. I'll merge the conversation.

@niieani niieani closed this as completed Feb 17, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants