4
votes

In this particular example, I'm extending the Array<T> interface like so:

interface BetterArray<T> extends Array<T> {
    push(this: BetterArray<T>, value: T): this;
}

Note for reference - Array<T>.push is implemented like this

interface Array<T> {
    push(...items: T[]): number;
}

But I get the following compile-time error:

Interface 'BetterArray' incorrectly extends interface 'T[]'.
Types of property 'push' are incompatible. Type '(this: BetterArray, value: T) => this' is not assignable to type '(...items: T[]) => number'. Type 'this' is not assignable to type 'number'. Type 'BetterArray' is not assignable to type 'number'.

Is there any way I can forcibly instruct TypeScript that I want to overwrite push on my interface (like member hiding in C#)?

Note - I'm using TypeScript 2.0

Further reading - it appears that this is purely down to return type - basically I want to enforce by interface, a new return type...

interface A {
    fn(): number;
}

interface B extends A {
    fn(): this;
}

Interface 'B' incorrectly extends interface 'A'. Types of property 'fn' are incompatible. Type '() => this' is not assignable to type '() => number'. Type 'this' is not assignable to type 'number'. Type 'B' is not assignable to type 'number'.

2

2 Answers

4
votes

You just need to add the original signature of Array.push:

interface BetterArray<T> extends Array<T> {
    push(...items: T[]): number;
    push(this: BetterArray<T>, value: T): this;
}

The problem with what you want to do though is that if this is a BetterArray then you can not return this, for exampl:

class BetterArrayClass<T> extends Array<T> {
    push(...items: T[]): number;
    push(this: BetterArrayClass<T>, value: T): this;
    push(this: BetterArrayClass<T>, ...items: T[]): number | this {
        return this;
    }
}

Errors with:

Type 'BetterArrayClass' is not assignable to type 'number | this'.
Type 'BetterArrayClass' is not assignable to type 'this'.
this: BetterArrayClass

The reason for that can be seen in this simpler example:

class A {
    fn(this: A, num: number): this {
        return this; // Error: Type 'A' is not assignable to type 'this'. this: A
    }
}

class B extends A {
    fn(num: number): this {
        if (num < 0) {
            return super.fn(num);
        }
        return this;
    }
}

If in B.fn we're calling super.fn then the this is not A but B, and in any case we want to return the instance of B.
Which is not what A.fn describes.

0
votes

As per nitzan tomer's | answer

You just need to add the original signature of Array.push:

interface BetterArray<T> extends Array<T> {
    push(...items: T[]): number;
    push(this: BetterArray<T>, value: T): this;
}

Using the never type seems to work for this. Intellisense picks up that never, "never" returns, so doesn't bother showing you the method signature (neat)

interface BetterArray<T> extends Array<T> {
    push(...items: T[]): never;
    push(this: BetterArray<T>, value: T): this;
}