Description
This is a suggestion for spec change.
Problem
Sometimes type's members could only be applicable with some restrictions of enclosing type parameter (generic type constraints).
For instance, this is use case of RxJS:
var xs: Observable<Observable<string>>;
xs.mergeAll(); // mergeAll is applicable here
var ys: Observable<string>;
ys.mergeAll(); // mergeAll is NOT applicable here
Original Rx.NET Merge method implemented as C# extension method on type IObservable<IObservable<T>>
. In RxJS it's implemented as an instance method.
Current RxJS typescript definition just use unsafe trick:
export interface Observable<T> {
...
mergeAll(): T;
...
}
But it can be still unsafely called for as instance of any Observable<T>
.
Possible solutions
Extension methods
Something like C# extension methods. But don't think it's useful in such cases.
declare extensions {
mergeAll<T>(this Observable<Observable<T>> source): Observable<T>;
}
BTW, extensions method could be other cool feature, which change call method (from instance-like to static-like).
Multiple interface definitions with different constraints
interface A<T> { x(): void; }
interface A<T extends B> { y(): void; }
var a: A<number>;
var b: A<B>;
a.x(); b.x(); // ok
b.y(); // ok
a.y(); // error
Member-level constraints with reference to enclosing type arguments
interface A<T> {
x(): void;
y<T extends B>(): void; // only applicable if T extends B
z<T extends B>: number; // for any property
// alternative syntax options:
<T extends B>z: number; // 1
z: number where T extends B; // 2
y(): void where T extends B;
}
I'll suggest this option. Moreover the previous solution options is special case of this one (incompatible constraints are merged on member level).